ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/bin/igsueme
Revision: 1.3
Committed: Tue Jun 21 10:36:47 2005 UTC (18 years, 11 months ago) by root
Branch: MAIN
Changes since 1.2: +4 -4 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #!/opt/bin/perl
2 package util;
3
4 sub format_time($) {
5 my ($time) = @_;
6
7 $time > 60*60
8 ? sprintf "%d:%02d:%02d", $time / (60 * 60), $time / 60 % 60, $time % 60
9 : sprintf "%d:%02d", $time / 60 % 60, $time % 60;
10 }
11
12
13 package goclock;
14
15 use Time::HiRes ();
16
17 use Gtk2;
18 use Glib::Object::Subclass Gtk2::Label;
19
20 sub INIT_INSTANCE {
21 my $self = shift;
22
23 $self->signal_connect (destroy => sub { $_[0]->stop });
24
25 $self->{format} = sub {
26
27 if ($_[0] < 0 || $self->{interval} < 0 || $self->{moves} < 0) {
28 util::format_time $_[0];
29
30 } else {
31 my $time = int $_[0];
32
33 if ($self->{interval} != 0) {
34 $time = int (($_[0] - 1) % $self->{interval} + 1);
35 }
36
37 sprintf "%s/%d = %d [s/mv]",
38 util::format_time $time,
39 $self->{moves},
40 $self->{moves} >= 1 ? $time / $self->{moves} : $self->{interval};
41 }
42 }
43
44 }
45
46 sub FINALIZE_INSTANCE {
47 my $self = shift;
48
49 $self->stop;
50 }
51
52 sub refresh {
53 my ($self) = @_;
54
55 my $timer = $self->{time};
56 print "TIM: $self->{time} $self->{moves} $self->{interval}\n";
57
58 $self->set_text ($self->{format}->($timer));
59 }
60
61 sub set_time {
62 my ($self, $start, $time, $moves, $interval) = @_;
63
64 $self->{interval} = $interval;
65 $self->{time} = $time;
66 $self->{moves} = $moves;
67
68 $self->refresh;
69 if ($start) {
70 $self->start;
71 } else {
72 $self->stop;
73 }
74 }
75
76 sub start {
77 my ($self, $interval) = @_;
78
79 $self->stop;
80
81 my $timeout; $timeout = sub {
82 $self->{timeout} = add Glib::Timeout 1000, $timeout;
83 $self->{time}--;
84 $self->refresh;
85 0;
86 };
87
88 $self->{time}++;
89 $timeout->();
90 }
91
92 sub stop {
93 my ($self) = @_;
94
95 remove Glib::Source delete $self->{timeout} if $self->{timeout};
96 }
97
98
99 package main;
100
101 use Glib;
102 use Gtk2 -init;
103 use Gtk2::Helper;
104 use Gtk2::SimpleList;
105 use Gtk2::GoBoard;
106 use Games::Go::SimpleBoard;
107 use IO::Socket::INET;
108 use Net::IGS;
109 use strict;
110
111 require Data::Dumper;
112
113 my %games;
114
115 my @timers;
116
117 my %alphmap = (
118 A => 1,
119 B => 2,
120 C => 3,
121 D => 4,
122 E => 5,
123 F => 6,
124 G => 7,
125 H => 8,
126 J => 9,
127 K => 10,
128 L => 11,
129 M => 12,
130 N => 13,
131 O => 14,
132 P => 15,
133 Q => 16,
134 R => 17,
135 S => 18,
136 T => 19,
137 U => 20,
138 V => 21,
139 W => 22,
140 );
141
142 sub dump($) {
143 print Data::Dumper::Dumper ([$_[0]]);
144 }
145
146 sub con {
147 my ($host, $port) = @_;
148
149 my $socket = new IO::Socket::INET PeerAddr => $host, PeerPort => $port
150 or die "cannot connect to $host:$port: $!";
151
152 $socket->autoflush (1);
153 return $socket;
154 }
155
156 sub spawn_game_view {
157 my ($igs, $gamenr) = @_;
158
159 my $game = $games{$gamenr};
160 my $size = $game->{size};
161
162 return if not defined $game;
163
164 my $w = Gtk2::Window->new;
165 $w->set_default_size (600, 700);
166
167 $w->add (my $v = new Gtk2::VBox);
168 $v->pack_start ((new Gtk2::Label
169 "Game $gamenr: White: $game->{white} [$game->{white_rank}] vs. Black: $game->{black} [$game->{black_rank}]"), 0, 1, 0);
170 $v->pack_start ((new Gtk2::Label
171 "Size: $game->{size} Handicap: $game->{handicap}"), 0, 1, 0);
172 $v->pack_start ((my $clock1 = new goclock), 0, 1, 0);
173 $v->pack_start ((my $clock2 = new goclock), 0, 1, 0);
174 $v->pack_start ((my $msg = new Gtk2::Label), 0, 1, 0);
175 $v->pack_start ((my $mv_lbl = new Gtk2::Label), 0, 1, 0);
176
177 $v->pack_start ((my $board = new Gtk2::GoBoard size => $size), 1, 1, 0);
178
179
180 $msg->set_text ($game->{title});
181
182 my $p3 = $igs->set_ev_cb ('game_title' => sub {
183 my ($s, $gnr, $t) = @_;
184 $gnr == $gamenr or return;
185 $msg->set_text ($t);
186 $game->{title} = $t;
187 });
188
189 my $pos = $igs->set_ev_cb ('game_over' => sub {
190 my ($self, $gnr, $result) = @_;
191 $gnr == $gamenr or return;
192 $clock1->stop;
193 $clock2->stop;
194 $msg->set_text ($game->{title} . ": Game Over: $result");
195 });
196
197 my $pos2 = $igs->set_ev_cb ('game_move' => sub {
198 my $self = shift;
199 my $gnr = shift;
200
201 $gnr == $gamenr or return;
202
203 my $move = shift;
204
205 my $mnr = $move->{number};
206 $move->{coords} =~ m/^(.)(\d+)/;
207 my $x = $alphmap{$1} - 1;
208 my $y = $size - $2;
209
210 my $white_mv = $move->{color} eq 'white';
211
212 $game->{moves}->[$mnr] = $move;
213 my $mv = $game->{moves_struct}->[$mnr]
214 = [ $x, $y, ~MARK_CIRCLE, MARK_CIRCLE | MARK_MOVE | ($white_mv ? MARK_W : MARK_B), '' ];
215
216 print "MOVE $gamenr | $mnr | $gnr : $x, $y $move->{color} = $move->{white_time} <=> $move->{black_time} | $move->{white_stones} <-> $move->{black_stones} | $game->{byo}\n";
217
218 $mv_lbl->set_text ("Move " . ($mnr + 1));
219 $clock1->set_time ((not $white_mv), $move->{white_time}, $move->{white_stones}, $game->{byo} * 60);
220 $clock2->set_time ($white_mv, $move->{black_time}, $move->{black_stones}, $game->{byo} * 60);
221 if ($move->{white_time} == 0 && $move->{white_stones} == -1 && $move->{black_time} == 0 && $move->{black_stones} == -1) {
222 $clock1->stop ();
223 $clock2->stop ();
224 }
225
226 my $board_state = new Games::Go::SimpleBoard $size;
227 $board_state->update ($game->{moves_struct});
228 $board->set_board ($board_state);
229 });
230
231 $w->signal_connect (destroy => sub {
232 $igs->unset_ev_cb ('game_over', $pos);
233 $igs->unset_ev_cb ('game_move', $pos2);
234 $igs->unset_ev_cb ('game_title', $p3);
235 $igs->feed_event ('req_unobserve', $gamenr);
236 });
237
238 $w->show_all;
239 }
240
241 sub build_ui {
242 my $igs = shift;
243
244 my $win = Gtk2::Window->new;
245 $win->set_default_size (600, 800);
246
247 my $slist = Gtk2::SimpleList->new (
248 'N' => 'int', # game number
249 'White' => 'text',
250 'W Rank' => 'text',
251 'Black' => 'text',
252 'B Rank' => 'text',
253 'Move' => 'int',
254 'Size' => 'int',
255 'Hand' => 'int',
256 'Komi' => 'double',
257 'Byo' => 'int',
258 'F' => 'text',
259 'R' => 'text',
260 );
261
262 my $plist = Gtk2::SimpleList->new (
263 'Name' => 'text',
264 'Rank' => 'text',
265 'Obs' => 'int',
266 'Pla' => 'int',
267 'Idle' => 'text',
268 'Flags' => 'text',
269 );
270
271 $win->add (my $p = Gtk2::VPaned->new);
272 $p->add1 (my $sw = Gtk2::ScrolledWindow->new);
273 $sw->add ($slist);
274 $p->add2 (my $hp = Gtk2::HPaned->new);
275 $hp->add1 (my $sw = Gtk2::ScrolledWindow->new);
276 $sw->add_with_viewport (my $v = Gtk2::VBox->new);
277 $v->pack_start (my $txt = Gtk2::TextView->new, 1, 1, 0);
278 $v->pack_start (my $b1 = Gtk2::Button->new ('sort'), 0, 1, 0);
279 $hp->add2 (my $sw = Gtk2::ScrolledWindow->new);
280 $sw->add ($plist);
281
282 $b1->signal_connect (clicked => sub {
283 @{$slist->{data}} =
284 map { my %g = %{$games{$_}}; print "$g{number}\n"; [ @g{qw/number white white_rank black black_rank move size handicap komi byo Fflag Rflag/} ] }
285 sort { $a <=> $b } keys %games;
286 });
287 $slist->signal_connect (row_activated => sub {
288 my ($sl, $path, $column) = @_;
289 my $row_ref = $sl->get_row_data_from_path ($path);
290
291 spawn_game_view ($igs, $row_ref->[0]);
292
293 $igs->feed_event ("req_observe", $row_ref->[0]);
294 $igs->feed_event ("req_moves", $row_ref->[0]);
295 });
296
297 $p->set_position (400);
298 $hp->set_position (300);
299
300 $igs->set_ev_cb ('game_list' => sub {
301 my %game = %{$_[1]};
302 $games{$game{number}} = { %game };
303 push @{$slist->{data}}, [ @game{qw/number white white_rank black black_rank move size handicap komi byo Fflag Rflag/} ];
304 });
305
306 $igs->set_ev_cb ('player_list' => sub {
307 my %player = %{$_[1]};
308 push @{$plist->{data}}, [ @player{qw/name rank obs playing idle flags/} ];
309 # @{$plist->{data}} = sort { $a->[0] cmp $b->[0] } @{$plist->{data}};
310 });
311
312 $win->show_all ();
313 }
314
315 #################################################################################
316 #################################################################################
317 #################################################################################
318
319 my $igs = new Net::IGS;
320
321 $igs->init ("elmex", "lolfe123");
322
323 my $sck = con ("igs.joyjoy.net", 6969);
324
325 $igs->set_writer (sub { $sck->syswrite ($_[0]) });
326
327 $igs->set_ev_cb ('recv_file' => sub {
328 print "FILE [ $_[1] ]\n";
329 });
330
331 $igs->sendline ('games');
332 $igs->sendline ('players');
333 my $buffer;
334
335 my $pos = $igs->set_ev_cb ('game_over' => sub {
336 my ($self, $gnr, $result) = @_;
337 delete $games{$gnr};
338 });
339
340 Gtk2::Helper->add_watch (fileno($sck), 'in', sub {
341 my $data;
342
343 my $r = $sck->sysread ($data, 4096);
344
345 if (not $r) {
346 # check $r == 0 or undef # FIXME
347 die "eof from server";
348 }
349
350 $buffer .= $data;
351 $igs->feed_data (\$buffer);
352
353 1;
354 });
355
356 build_ui ($igs);
357
358 Gtk2->main;