1
2010
年度 機械工学総合演習第二
計算機演習
B3 GUI
アプリケーション
担当:谷川智洋 講師,西村邦裕 助教
TA
:梶波 崇,木村 健太郎,竹内 俊貴,山崎 充彦,
http://www.cyber.t.u-tokyo.ac.jp/˜tani/class/mech enshu/
2010
年
7
月
6
日
1
演習の目的
本演習では,X Window System上で動作するGUI(Graphical User Interface)のプログラムを作成するこ とで,GUIを扱う上で不可欠なイベント駆動型(event driven)プログラミングの考え方を学習する.また, 画像ファイルの取り扱いやネットワークプログラムの作成を通して,最終的に普段我々が使っているアプリ ケーションに近いグラフィカルなプログラム作成を目指す.
前回は,C言語とGUI(Graphical User Interface)ツールキット・ライブラリを用いた,X Window System
上で動作する画像ファイルの取り扱いなどを通して,GUIプログラミングの基礎を学んだ.本演習では,こ れまで学習したプログラムにGUIを付け加えることで,最終的に普段我々が使っているアプリケーションに 近いマルチクライアントチャットやバトルシップアプリケーションの作成を目指す.
2
画像処理アプリケーションの作成
前回行ったGUIプログラミングを応用して,GUIによる画像処理プログラムを作成する.2.1
描画のためのウィジェット
(
再
)
GTKはGDK (GIMP Drawing Kit)のうえに実装されており,GDKは基本的には基礎となるウィンドウ 関数(X Window systemではXLibにあたる)へアクセスする低レベル関数を包むラッパー(Wrapper)であ る.GDKのリファレンスとしては,http://www.gnome.gr.jp/docs/gtk+-1.2.x-refs/gdk/index.htmlを見 ると良い.
スクリーンへの描画の処理の為に使うウィジェットはDrawingArea ウィジェットである.描画領域ウィ ジェットは,空白のキャンバスでそこに好きなものを何でも描ける.描画領域ウィジェットは以下の呼び出し によって作成される.
GtkWidget* gtk_drawing_area_new ( void );
/* ウィジェットのデフォルトサイズはこの呼び出しで指定できる.*/ void gtk_drawing_area_size ( GtkDrawingArea *darea,
gint width, gint height); 描画領域を生成した後,次のシグナルに接続する必要がある場合がある. • ユーザ入力に応答するためのマウスとボタン押下シグナル • ウィジットが生成されて表示されたときに必要な処理を行う“realize” シグナル • ウィジットのサイズが変更されたときに必要な処理を行う “size allocate”シグナル • ウィジットの内容を再描画するための“expose event”シグナル RGB,白黒とカラーのイメージ(画像)をウィンドウのピクセルに変換して通常のウィンドウに表示するた め,GDKにはGdkRgbという呼び出しが用意されている.GdkRgbの機能を使用する前にgdk_rgb_init() 関数を呼び出す.もしこの処理に失敗すると,coreダンプする.(GtkPreviewを含む) GdkRgbを使用する 全てのGTK+ウィジットは自分のクラスの初期化メソッド(class_init)の中でgdk_rgb_init()関数を 呼び出す.よって,間接的にのみGdkRgbを使用する場合は,その呼び出しを考慮する必要はない.
void gdk_rgb_init (void);
void gdk_draw_rgb_image ( GdkDrawable *drawable, GdkGC *gc, gint x, gint y, gint width, gint height, GdkRgbDither dith, guchar *rgb_buf, gint rowstride );
2.2 ウィジェットからの情報の取得(再) 3
void gdk_draw_gray_image ( GdkDrawable *drawable, GdkGC *gc, gint x, gint y, gint width, gint height, GdkRgbDither dith, guchar *buf, gint rowstride );
drawable : 描画する GdkDrawable (通常は GdkWindow)
gc : グラフィックス・コンテキスト(全てのGDK描画関数が必要とするもの 中身は無視される) x : drawable の左上端の X座標 y : drawable の左上端の Y座標 width : 描画する矩形の幅 height : 描画する矩形の高さ dith : GdkRgbDither の値で、お好みのディザ・モードを選択する rgb_buf : ピクセル・データでPacked24ビットデータとして表現される (Packed : ビットの格納方式の一つ.一ピクセルにつきDepthを 並べて格納する) rowstride : rgb_buf である桁の始点から次の桁が開始する点までのバイト数
gdk_draw_rgb_image () は Drawable の 中 に RGB 画 像 を 描 画 し ,gdk_draw_gray_image () は
Drawable の中に白黒画像を描画する.引数rowstride はより柔軟に線を配置するためのもので,一般的に,
0 <= i < widthと0 <= j < heightで,pixel(x+i, y+j)は赤(R) = rgb_buf[j*rowstride + i*3], 緑(G) = rgb_buf[j*rowstride + i*3 + 1],青(B) = rgb_buf[j*rowstride + i*3 + 2] となる.
2.2
ウィジェットからの情報の取得
(
再
)
また,GUIによるインタラクティブなプログラムを作るためには,ボタンのような押す/離すの単純な入力 だけでなく,文字列や数値など,より複雑な情報を入力できるようにする必要がある.ここでは,文字列の入 力方法としてentryウィジェット,数値の入力方法としてscaleウィジェットの使い方を学ぶ. entryウィジェット 文字列を入力するためにはentryウィジェットを用いる.entryウィジェットの作成は以下のようなコード を書けば良い./* ウィジェットの宣言 */ GtkWidget *entry; /* 新しいentry ウィジェットの確保,文字数の設定 */ entry = gtk_entry_new_with_max_length (50); また,entryウィジェットの文字列を取り出すには以下のようにすればよい. gchar *entry_text;
entry_text = gtk_entry_get_text (GTK_ENTRY(entry));
scaleウィジェット 数値を入力するためには,entryウィジェットに直接数値を打ち込んでも良いが,よりGUIらしい入力方法 として,scaleウィジェットが用意されている.scaleウィジェットの使用方法はこれまでのウィジェットより 若干複雑になっている.まず,scaleウィジェットの範囲や初期値,増減値などを設定するための,adjustment オブジェクトを作成する.adjustmentオブジェクトを作成するための関数gtk_adjustment_new()の使い 方は以下の通りである.
GtkObject* gtk_adjustment_new ( gfloat value, gfloat lower, gfloat upper, gfloat step_increment, gfloat page_increment, gfloat page_size ); value : 初期値 lower : 最小値 upper : 最大値 step_increment : 増減値(小) page_increment : 増減値(大) page_size : スケールを移動するウィジェットの大きさ
upperは表示領域の最大値であり,値の最大値ではないことに注意せよ.値の最大値は upper - page_size
になる.
その後,これまでのウィジェットと同様にscaleウィジェットを宣言して確保し,先に作成したadjustment
2.3 画像の変換 5 /* オブジェクト,ウィジェットの宣言 */ GtkObject *adjustment; GtkWidget *scale; /* 新しいadjustment オブジェクトの確保 */ adjustment = gtk_adjustment_new(0, 0, 255, 1, 1, 0); /* コールバック関数の設定(ここではon_slider_moved()という関数をよそで定 義している) */ gtk_signal_connect(GTK_OBJECT(adjustment), "value_changed", GTK_SIGNAL_FUNC(on_slider_moved), adjustment); /* 新しい水平scale ウィジェットの確保,adjustment オブジェクトの指定 */ scale = gtk_vscale_new(GTK_ADJUSTMENT(adjustment)); /* 小数点以下の桁数の設定 */ gtk_scale_set_digits(GTK_SCALE(scale), 0); また,scaleウィジェットの値をコールバック関数から取り出すには以下のようにすればよい.
void on_slider_moved(GtkWidget * widget, GtkAdjustment *adj) { int scale_value; scale_value = adj->value; }
2.3
画像の変換
二値化 しきい値Tを定め,画像中の画素値がTより大きいときその画素値を1,それ以外のとき画素値を0にす る変換を画像の二値化(binarization)と呼ぶ. 二値化は,文書や図面のような白黒の画像をスキャナで濃淡画 像として入力した場合などに,濃淡画像から二値画像に変換するために行われる.また,一般の画像で対象物 と背景を分離するために二値化が行われることも多い. コントラスト変換 コントラスト変換は,画像が明るすぎたり暗すぎたりする場合や,微妙に濃淡を変化させたいときなどに用 いられる.画像の濃淡を一定の規則に従って変換するため,コントラストだけでなく階調性も変化する. もっとも簡単な変換は,画像の濃淡を目的に応じた適当な関数で変換する方法である.入力画像の濃淡レベ ルをxとすると,このxを目的に応じた関数fでy = f (x)に変換することで画像のコントラストの改善など を行う.画像の濃淡レベル域[xmin, xmax]が,一部分にしか分布していないような場合など,図1に示すように画像の濃淡レベル域をより広い濃淡レベル線形拡大する.変換関数は,以下の式で表される. xmax≥ xのとき,y = ymax xmin≥ x < xmaxのとき,y = ymax− ymin xmax− xmin (x− xmin) + ymin x < xminのとき,y = ymin xmin xmax ymin ymax 図1 線形変換の例
2.4
線形フィルタを用いた画像処理
次は3× 3の線形フィルタを実装しよう.線形フィルタは入力画像と重み行列(フィルタ)の単純な畳み込 み演算で画像に効果を与えることができる最も基本的な画像操作のひとつである.線形フィルタには一般に 3× 3や5× 5の行列が用いられ,行列の変更のみで効果が変わってくる.ここでは平滑化フィルタ,差分フィ ルタ(エッジ抽出)について簡単に説明する. 平滑化フィルタ 平滑化フィルタは,注目している画素の近傍の画素値の重みつき積分を,出力画素の画素値とし,主に入力 画像のノイズを除去するために使われる.最も単純なのは近傍画素の単純な平均値をとるフィルタで,3× 3のフィルタでは,f (i, j)を入力画像,g(i, j)を出力画像の画素(i, j)の画素値とすると,
g(i, j) = 1 9 1 ∑ m=−1 1 ∑ n=−1 f (i + m, j + n) で定義される.これは入力画像と行列 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 1 9 のコンボリューションをとる操作である.この単純な平均値を用いたフィルタは,注目する画素が,周囲の画 素にくらべて極端に大きい,あるいは小さい画素値をとる場合に,周囲の画素値に近づけてる効果をもつ.ノ イズは一般に近傍の画素と大きく異なる画素値を持つため,このフィルタを適用すれば,ノイズを目立たなく することができる.しかし,同時に画像上の輪郭もぼかしてしまうことになる.
2.4 線形フィルタを用いた画像処理 7 エッジ抽出フィルタ エッジ抽出フィルタは,注目画素の近傍の画素の変化率から,画像上の線や輪郭を抽出する.平滑化が積分 なのに対してこちらは微分演算にあたる.実際には近傍画素の差分をとるだけのことで,平滑化フィルタの足 し算が引き算に変わっただけである. ■微分フィルタ,Sobelフィルタ 垂直方向のエッジを抽出する場合,入力画像と以下の行列のコンボリュー ションを計算すればよい. −1 0 1−1 0 1 −1 0 1 これを1次線形微分フィルタと呼ぶ.更に,注目画素に近い近傍画素ほど重みをつけたものもある.つまり 行列 −1 0 1−2 0 2 −1 0 1 をフィルタに用いるのである.これをSobelフィルタという. ■ラプラシアンフィルタ 画素の輝度値の1階微分によるフィルタでは,画像上の輪郭は,隣り合った画素の 輝度差が大きいところである,という仮定が元になっている.しかし,実際は輪郭以外でも輝度値がなだらか に変化している部分も存在し,必ずしも1次線形微分フィルタで輪郭を抽出できるとはいえない.そこで,輝 度値の変化自体が大きく変化する部分を輪郭と仮定して輪郭抽出を行なうという考え方がでてくる. すなわち輝度分布の2階微分であるラプラシアンを計算することになる.(図2). 実際のラプラシアンの計算は,これまで同様に3× 3のマトリクスとのコンボリューションで表現できる. 輝度値の分布がf (i, j)である画像の,輝度値の水平方向,垂直方向の2階微分をそれぞれ, ∂2
∂i2f (i, j)≡ {f(i + 1, j) − f(i, j)} − {f(i, j) − f(i − 1, j)} ∂2
∂j2f (i, j)≡ {f(i, j + 1) − f(i, j)} − {f(i, j) − f(i, j − 1)}
と定義できるとき,画像のラプラシアン ∂2 ∂i2f (i, j) + ∂2 ∂j2f (i, j) を求めるフィルタを3× 3で表現し,実際の画像でエッジ検出ができる.
図2 輝度値が変化しているところでは,(A),(B)で変化の度合い(微分係数)は同じであるが,(B)のよ うに局所的に輝度値が変化しない限り輪郭(エッジ)とはならない.つまり輝度値の分布の微分では必ずし もエッジを抽出できるとは限らないが,2階微分すればより精度よく抽出できることがわかる. ◇ 課題1◇ 1. sample5.cをもとに,scaleウィジェットを用いて閾値を指定し画像を二値化せよ(図3). 2. sample5.c内の関数image_filter()を書き換え,上の平滑化フィルタを,3× 3の配列を 用いて実装せよ.(図4). 3. 水平・垂直方向のエッジを強調するSobelフィルタを作成し,上のフィルタと結果を比較せ よ.(オプション) 4. ラプラシアンフィルタを作成し,上のフィルタと結果を比較せよ.(オプション) 図3 課題1.1実行時の画面 図4 課題1.2実行時の画面
9
3
マルチクライアントテキストチャット
マルチクライアントチャットの回において,ネットワーク越しに複数人の人が同時にテキストチャットを作 成した.本演習では,クライアントのGUI版の作成と対応するサーバの作成を通して,GUIプログラムとソ ケット通信の復習を行う. 一般に,グラフィカルユーザインタフェースを持つシングルスレッドのアプリケーションでは,時間が掛 かる処理を行うためのボタンが押されたら“待ちカーソル”(砂時計など)を表示して,動かなくなる.アプリ ケーションをマルチスレッド化することで,アプリケーションとユーザインタフェースを多くのリクエストに 応答することができる.長時間かかる処理のボタンが押されたら,独立したスレッドにその処理を行わせ,そ の間もユーザーインタフェースは別スレッドで実行を続けることができる.チャットプログラムでは,ネッ トワーク通信を独立したスレッドにすることで,GUIアプリケーションはネットワーク通信の状況によらず ユーザの入力を受け付けることができる.3.1
テキストチャットサーバ
複数人でネットワーク越しにチャットを実現するためには,サーバ側に複数のクライアントからのアクセス を受け付ける仕組みが必要となる.本演習では,マルチスレッドを利用してマルチクライアントチャットを実 現する.クライアントからサーバに接続要求があると,そのクライアントに対応する専用スレッドを生成し, その専用スレッドにチャットデータの送受信を個別に担当させる(図5). ᡛӖ̮ǹȬȃȉᲫ ᡛӖ̮ǹȬȃȉᲬ ᡛӖ̮ǹȬȃȉ᳨ ዓฎLjǯȩǤǢȳȈऴإ ȞȫȁǯȩǤǢȳȈȁȣȃȈǵȸȐȗȭǻǹ Ӗ̮ǹȬȃȉ σஊȡȢȪ ȦȸǶλщ Ӗ˄ȗȭǻǹ ȁȣȃȈϋܾᘙᅆȗȭǻǹ ǯȩǤǢȳȈᚘምೞᲫ ǵȸȐᚘምೞ μȁȣȃȈϋܾ μȁȣȃȈϋܾ Ӗ̮ǹȬȃȉ σஊȡȢȪ ȁȣȃȈϋܾᘙᅆȗȭǻǹ ǯȩǤǢȳȈᚘምೞᲬ μȁȣȃȈϋܾ μȁȣȃȈϋܾ Ӗ̮ǹȬȃȉ σஊȡȢȪ ȁȣȃȈϋܾᘙᅆȗȭǻǹ ǯȩǤǢȳȈᚘምೞ᳨ μȁȣȃȈϋܾ μȁȣȃȈϋܾ ȦȸǶλщ Ӗ˄ȗȭǻǹ ȦȸǶλщӖ˄ȗȭǻǹ ǯȩǤǢȳȈऴإ ǯȩǤǢȳȈऴإ ǯȩǤǢȳȈऴإ ǽDZȃȈᡫ̮ ӲᚘምೞƔǒƷȁȣȃȈϋܾ ӲᚘምೞǁƷȁȣȃȈ ϋܾƷȖȭȸȉǭȣǹȈ 図5 コンソール版マルチクライアントテキストチャットのシステム図 この場合,サーバへのクライアントの接続数と同じ数だけスレッドが生成されることになる.サーバのメイ ン関数では,クライアントからの接続待ちと,スレッドの生成が主な仕事となる.また,サーバは,対応するクライアントからデータを受け取った時点で,サーバに接続しているクライアント全てにそのデータを配信す るようにする.接続したクライアント数と接続したクライアントのIDを管理しておき,接続されているクラ イアントごとに生成された専用スレッドはクライアントからデータを受け取るとID順に各クライアントへ送 信する.
3.2
マルチスレッド補足:スレッドへの引数の受け渡し
新しいスレッドのスタートルーチンに渡す引数は1個のポインタである.もっと多くの引数が必要なら,構 造体を準備し引数がそれを指すようにすればよい. struct two_args { int arg1; int arg2; }; void *needs_2_args(void *); void a() { pthread_t t;struct two_args *ap;
ap = (struct two_args *)malloc(sizeof (struct two_args)); ap->arg1 = 1;
ap->arg2 = 2;
error = pthread_create(&t, NULL, needs_2_args, (void *) ap); ....
}
void *needs_2_args(void *ap) {
struct two_args *argp = (struct two_args *)ap; int a1, a2;
a1 = argp->arg1; a2 = argp->arg2; free(argp); .... }
3.3
テキストチャットクライアント
ターミナルを使ったクライアントプログラムでは,ユーザが文字を入力している間でも,他のクライアント がチャット内容を送信するとその内容が表示されてしまう.マルチクライアントチャットの回では,文字入力 用のターミナルとチャット内容を表示するターミナルの二つのターミナルを準備し,チャット内容の表示が文 字入力に影響を与えないように改良した.入力用のターミナルでユーザからの文字の入力とチャット内容の送3.4 ソケット通信 11 受信を行い,受信したチャット内容の情報を,チャット内容表示用のターミナルに渡して,そのターミナルで 表示するようにする.ターミナル間では,プロセスが異なりメモリ空間が異なるので,プロセス間の通信に共 有メモリを用いて情報共有を行った(図5). 本演習では,前回の演習で学習したGUIプログラミングを用いて,文字入力とチャット内容の表示を一つ のプログラムで実現する(図6,図8).文字入力を待つ部分と相手からのソケットを通じたデータを待つ部分 は排他になるため,文字を入力し送信するまで,相手側からの文字を読み取ることはできない.そのため,相 手側から文字を受け取る部分にスレッドを使い,文字を入力する部分と相手から文字を受け取る部分の処理が 並列に動作するようにする.マルチスレッドの場合は,同一プロセス内での処理となるのでメモリ空間が共有 されており,グローバル変数を利用することで情報の共有が可能である. Ӗ̮ǹȬȃȉ ȁȣȃȈǯȩǤǢȳȈȗȭǻǹ ǯȩǤǢȳȈᚘምೞP μȁȣȃȈϋܾ ᡛ̮ǹȬȃȉ VGZVXKGY ȦȸǶλщϋܾ GPVT[YKFIGV Ӗ̮ǹȬȃȉ ȁȣȃȈǯȩǤǢȳȈȗȭǻǹ ǯȩǤǢȳȈᚘምೞ μȁȣȃȈϋܾ ᡛ̮ǹȬȃȉ VGZVXKGY ȦȸǶλщϋܾ GPVT[YKFIGV ᡛӖ̮ǹȬȃȉᲫ ᡛӖ̮ǹȬȃȉᲬ ᡛӖ̮ǹȬȃȉ᳨ ዓฎLjǯȩǤǢȳȈऴإ ȞȫȁǯȩǤǢȳȈȁȣȃȈǵȸȐȗȭǻǹ Ӗ̮ǹȬȃȉ ȁȣȃȈǯȩǤǢȳȈȗȭǻǹ ǯȩǤǢȳȈᚘምೞᲫ ǵȸȐᚘምೞ μȁȣȃȈϋܾ ǯȩǤǢȳȈऴإ ǯȩǤǢȳȈऴإ ǯȩǤǢȳȈऴإ ǽDZȃȈᡫ̮ ӲᚘምೞƔǒƷȁȣȃȈϋܾ ӲᚘምೞǁƷȁȣȃȈ ϋܾƷȖȭȸȉǭȣǹȈ ᡛ̮ǹȬȃȉ VGZVXKGY ȦȸǶλщϋܾ GPVT[YKFIGV 図6 GUI版マルチクライアントテキストチャットのシステム図 図7 テキストチャットクライアントの起動画面
3.4
ソケット通信
なお,ソケット通信では,大量のデータを一度に送受信しようとしてもうまく動作しないことがある.一 度に送受信できるデータのサイズはネットワークの混雑状態などに依存しており,問題がなければ一度に 100Kbyte 送受信できることもあるが,混雑しているときは1byteしか送受信することができない場合もあ る.次に行う音声データの送受信には,送るデータのサイズがテキストの送受信の場合にくらべ格段に大き図8 テキストチャットクライアントの実行画面
くなり,分割して送信する必要がある.このため,readでの受信,writeでの受信,の際,その返り値を参 照して再送(または再受信) を行う必要がある.例えば,256byte を送信したい場合に,write の返り値が
100だった場合,残り156byteが未送信状態なので,このデータを再送信する必要がある.socket read()と
socket write()は,この仕組みを実装したコードである.
3.5
パッキングボックス復習
より多機能なGUIプログラムを作るためには、複数のウィジェットを思い通りの位置に配置する必要があ る.Gtk+をはじめとして多くのGUIツールキットでは“パッキング”という操作によってウィジェットの 配置を行なう.パッキングの操作では,“見えない箱”の中にウィジェットを詰め(=パック),さらにその箱 をより大きな箱に詰めていって,多数のウィジェットを高率良くウィンドウの中に整理して並べるのが基本で ある. テキストチャットのGUI 版クライアントの場合,図8 のようなレイアウトを実現するために,まず,entry boxというパッキングボックスにentryウィジェットと送信ボタンを順番にパッキングする.次に, ウィンドウ全体にまたがるmain boxというパッキングボックスに,テキストを表示するためのtext view
ウィジェットと,入れ子になるように先に宣言したentry boxパッキングボックスをパックする(図9). Client text_view entry_msg button_send main_box entry_box 図9 テキストチャットクライアントのパッキングボックス
3.5 パッキングボックス復習 13
◇ 課題2◇
複数間のテキストチャットを行えるようなプログラムtext chat server とtext chat clientのGUI
版 を作成せよ.お互いにテストする人がいない場合,自分の計算機上でサーバとクライアントを起 動してチェックせよ.client 127.0.0.1とすると自分の計算機上で起動したサーバに接続できる. 1. server.cのTODOをうめ,実行せよ. 2. client.cのTODOをうめ,実行せよ(図7,図8). 3. チャットの文章を提示するとき,誰が話しているかも同時に表示せよ.
4
バトルシップの
GUI
アプリケーション
(
オプション
)
4.1
バトルシップゲームについて
すでに説明がありましたが,バトルシップゲーム(軍艦ゲーム)とは,2人のプレイヤーがそれぞれのマ ス状の自陣に軍艦を配置し,相手陣の配置を推測しながら, どちらかの陣が壊滅するまで, 相手陣へ攻撃を行 うゲームです.バトルシップゲームは,2人のプレイヤーがそれぞれ以下の手順ですすめる.また,具体的な マップは図10)のようになる. • 9×9マスの自陣に船を配置する(お互いに秘密) • 先攻,後攻を決め,順番に相手陣のマスを指定して攻撃を行う • 攻撃を受けた側は,相手の攻撃の結果(命中,はずれ,命中した場合は船の種類も)を相手に知らせる • 攻撃を繰り返し,先に相手側の全ての船を撃沈したほうが勝ち. • 船は複数マスからなるが,撃沈のためには全てのマスに攻撃を命中させねばならない. 図10 Linux上でのバトルシッププログラム実行画面 また,戦艦1隻,巡洋艦2隻,駆逐艦3隻,潜水艦4隻の軍艦を自陣に配置する(表1).なお,配置のルー ルは以下の通りとなる. • 2マス以上の船は直列に並べなくてはならない • 各船は接してはいけない. 縦横だけでなく,斜めで接してもいけない. • 4隅の端の3マスは岩(rock)であるとして船を配置できない. アルゴリズムの演習に使ったため,演習では探索のアルゴリズムの部分のみでインタフェースについては触 れていなかった.本演習では,ネットワークを介したバトルシップゲームの対戦を行うアプリケーションを課 題とする.同機能を持つクライアントとサーバが相互に接続し,送受信される戦艦の有無,攻撃の結果をやり とりする.最初に軍艦の配置を行い(図11),交互に,攻撃するマスを選択して(図12,図13),対戦を行う.4.1 バトルシップゲームについて 15 和名 英語名 記号 マス数 配置数 戦艦 Battleship B 4 1 巡洋艦 Cruiser C 3 2 駆逐艦 Destroyer D 2 3 潜水艦 Submarine S 1 4 表1 バトルシップの軍艦リスト ◇ 課題3◇ サーバ・クライアント間のバトルシップ対戦を行えるプログラムを作成せよ.お互いにテスト する人がいない場合,自分の計算機上でサーバとクライアントを起動してチェックせよ.client 127.0.0.1とすると自分の計算機上で起動したサーバに接続できる. 1. server.cのTODOをうめ,実行せよ. 2. client.cのTODOをうめ,実行せよ(図11,図12,図13). 図11 軍艦の配置画面.ラジオボタンで軍艦の種類と向きを選択して配置する
図12 対戦画面.攻撃するマスを選択する.