ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/bin/igsueme
Revision: 1.4
Committed: Sun Jul 3 19:35:15 2005 UTC (18 years, 10 months ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +10 -25 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, B => 2, C => 3, D => 4,
119 E => 5, F => 6, G => 7, H => 8,
120
121 J => 9, K => 10, L => 11, M => 12,
122 N => 13, O => 14, P => 15, Q => 16,
123 R => 17, S => 18, T => 19, U => 20,
124 V => 21, W => 22,
125 );
126
127 sub dump($) {
128 print Data::Dumper::Dumper ([$_[0]]);
129 }
130
131 sub con {
132 my ($host, $port) = @_;
133
134 my $socket = new IO::Socket::INET PeerAddr => $host, PeerPort => $port
135 or die "cannot connect to $host:$port: $!";
136
137 $socket->autoflush (1);
138 return $socket;
139 }
140
141 sub spawn_game_view {
142 my ($igs, $gamenr) = @_;
143
144 my $game = $games{$gamenr};
145 my $size = $game->{size};
146
147 return if not defined $game;
148
149 my $w = Gtk2::Window->new;
150 $w->set_default_size (600, 700);
151
152 $w->add (my $v = new Gtk2::VBox);
153 $v->pack_start ((new Gtk2::Label
154 "Game $gamenr: White: $game->{white} [$game->{white_rank}] vs. Black: $game->{black} [$game->{black_rank}]"), 0, 1, 0);
155 $v->pack_start ((new Gtk2::Label
156 "Size: $game->{size} Handicap: $game->{handicap}"), 0, 1, 0);
157 $v->pack_start ((my $clock1 = new goclock), 0, 1, 0);
158 $v->pack_start ((my $clock2 = new goclock), 0, 1, 0);
159 $v->pack_start ((my $msg = new Gtk2::Label), 0, 1, 0);
160 $v->pack_start ((my $mv_lbl = new Gtk2::Label), 0, 1, 0);
161
162 $v->pack_start ((my $board = new Gtk2::GoBoard size => $size), 1, 1, 0);
163
164
165 $msg->set_text ($game->{title});
166
167 my $p3 = $igs->set_ev_cb ('game_title' => sub {
168 my ($s, $gnr, $t) = @_;
169 $gnr == $gamenr or return;
170 $msg->set_text ($t);
171 $game->{title} = $t;
172 });
173
174 my $pos = $igs->set_ev_cb ('game_over' => sub {
175 my ($self, $gnr, $result) = @_;
176 $gnr == $gamenr or return;
177 $clock1->stop;
178 $clock2->stop;
179 $msg->set_text ($game->{title} . ": Game Over: $result");
180 });
181
182 my $pos2 = $igs->set_ev_cb ('game_move' => sub {
183 my $self = shift;
184 my $gnr = shift;
185
186 $gnr == $gamenr or return;
187
188 my $move = shift;
189
190 my $mnr = $move->{number};
191 $move->{coords} =~ m/^(.)(\d+)/;
192 my $x = $alphmap{$1} - 1;
193 my $y = $size - $2;
194
195 my $white_mv = $move->{color} eq 'white';
196
197 $game->{moves}->[$mnr] = $move;
198 my $mv = $game->{moves_struct}->[$mnr]
199 = [ $x, $y, ~MARK_CIRCLE, MARK_CIRCLE | MARK_MOVE | ($white_mv ? MARK_W : MARK_B), '' ];
200
201 print "MOVE $gamenr | $mnr | $gnr : $x, $y $move->{color} = $move->{white_time} <=> $move->{black_time} | $move->{white_stones} <-> $move->{black_stones} | $game->{byo}\n";
202
203 $mv_lbl->set_text ("Move " . ($mnr + 1));
204 $clock1->set_time ((not $white_mv), $move->{white_time}, $move->{white_stones}, $game->{byo} * 60);
205 $clock2->set_time ($white_mv, $move->{black_time}, $move->{black_stones}, $game->{byo} * 60);
206 if ($move->{white_time} == 0 && $move->{white_stones} == -1 && $move->{black_time} == 0 && $move->{black_stones} == -1) {
207 $clock1->stop ();
208 $clock2->stop ();
209 }
210
211 my $board_state = new Games::Go::SimpleBoard $size;
212 $board_state->update ($game->{moves_struct});
213 $board->set_board ($board_state);
214 });
215
216 $w->signal_connect (destroy => sub {
217 $igs->unset_ev_cb ('game_over', $pos);
218 $igs->unset_ev_cb ('game_move', $pos2);
219 $igs->unset_ev_cb ('game_title', $p3);
220 $igs->feed_event ('req_unobserve', $gamenr);
221 });
222
223 $w->show_all;
224 }
225
226 sub build_ui {
227 my $igs = shift;
228
229 my $win = Gtk2::Window->new;
230 $win->set_default_size (600, 800);
231
232 my $slist = Gtk2::SimpleList->new (
233 'N' => 'int', # game number
234 'White' => 'text',
235 'W Rank' => 'text',
236 'Black' => 'text',
237 'B Rank' => 'text',
238 'Move' => 'int',
239 'Size' => 'int',
240 'Hand' => 'int',
241 'Komi' => 'double',
242 'Byo' => 'int',
243 'F' => 'text',
244 'R' => 'text',
245 );
246
247 my $plist = Gtk2::SimpleList->new (
248 'Name' => 'text',
249 'Rank' => 'text',
250 'Obs' => 'int',
251 'Pla' => 'int',
252 'Idle' => 'text',
253 'Flags' => 'text',
254 );
255
256 $win->add (my $p = Gtk2::VPaned->new);
257 $p->add1 (my $sw = Gtk2::ScrolledWindow->new);
258 $sw->add ($slist);
259 $p->add2 (my $hp = Gtk2::HPaned->new);
260 $hp->add1 (my $sw = Gtk2::ScrolledWindow->new);
261 $sw->add_with_viewport (my $v = Gtk2::VBox->new);
262 $v->pack_start (my $txt = Gtk2::TextView->new, 1, 1, 0);
263 $v->pack_start (my $b1 = Gtk2::Button->new ('sort'), 0, 1, 0);
264 $hp->add2 (my $sw = Gtk2::ScrolledWindow->new);
265 $sw->add ($plist);
266
267 $b1->signal_connect (clicked => sub {
268 @{$slist->{data}} =
269 map { my %g = %{$games{$_}}; print "$g{number}\n"; [ @g{qw/number white white_rank black black_rank move size handicap komi byo Fflag Rflag/} ] }
270 sort { $a <=> $b } keys %games;
271 });
272 $slist->signal_connect (row_activated => sub {
273 my ($sl, $path, $column) = @_;
274 my $row_ref = $sl->get_row_data_from_path ($path);
275
276 spawn_game_view ($igs, $row_ref->[0]);
277
278 $igs->feed_event ("req_observe", $row_ref->[0]);
279 $igs->feed_event ("req_moves", $row_ref->[0]);
280 });
281
282 $p->set_position (400);
283 $hp->set_position (300);
284
285 $igs->set_ev_cb ('game_list' => sub {
286 my %game = %{$_[1]};
287 $games{$game{number}} = { %game };
288 push @{$slist->{data}}, [ @game{qw/number white white_rank black black_rank move size handicap komi byo Fflag Rflag/} ];
289 });
290
291 $igs->set_ev_cb ('player_list' => sub {
292 my %player = %{$_[1]};
293 push @{$plist->{data}}, [ @player{qw/name rank obs playing idle flags/} ];
294 # @{$plist->{data}} = sort { $a->[0] cmp $b->[0] } @{$plist->{data}};
295 });
296
297 $win->show_all ();
298 }
299
300 #################################################################################
301 #################################################################################
302 #################################################################################
303
304 my $igs = new Net::IGS;
305
306 $igs->init ("elmex", "lolfe123");
307
308 my $sck = con ("igs.joyjoy.net", 6969);
309
310 $igs->set_writer (sub { $sck->syswrite ($_[0]) });
311
312 $igs->set_ev_cb ('recv_file' => sub {
313 print "FILE [ $_[1] ]\n";
314 });
315
316 $igs->sendline ('games');
317 $igs->sendline ('players');
318 my $buffer;
319
320 my $pos = $igs->set_ev_cb ('game_over' => sub {
321 my ($self, $gnr, $result) = @_;
322 delete $games{$gnr};
323 });
324
325 Gtk2::Helper->add_watch (fileno($sck), 'in', sub {
326 my $data;
327
328 my $r = $sck->sysread (my $data, 4096);
329
330 if (not $r) {
331 # check $r == 0 or undef # FIXME
332 die "eof from server";
333 }
334
335 $igs->feed_data ($data);
336
337 1;
338 });
339
340 build_ui ($igs);
341
342 Gtk2->main;
343