package KGS::Game::Tree;
use Gtk2::GoBoard::Constants;
use KGS::Constants;
# exclusion masks... the bit on the left excludes (removes) the ones on the right
my %exclude_type = (
&MARK_TRIANGLE => MARK_SQUARE | MARK_TRIANGLE | MARK_CIRCLE | MARK_LABEL,
&MARK_SQUARE => MARK_SQUARE | MARK_TRIANGLE | MARK_CIRCLE | MARK_LABEL,
&MARK_CIRCLE => MARK_SQUARE | MARK_TRIANGLE | MARK_CIRCLE | MARK_LABEL,
&MARK_LABEL => MARK_SQUARE | MARK_TRIANGLE | MARK_CIRCLE | MARK_LABEL,
&MARK_SMALL_B => MARK_SMALL_B | MARK_SMALL_W | MARK_MOVE,
&MARK_SMALL_W => MARK_SMALL_B | MARK_SMALL_W | MARK_MOVE,
&MARK_GRAYED => MARK_MOVE | MARK_GRAYED,
&MARK_B => MARK_B | MARK_W | MARK_MOVE | MARK_GRAYED, #d# was !MARK_GRAYED here
&MARK_W => MARK_B | MARK_W | MARK_MOVE | MARK_GRAYED, #d# was !MARK_GRAYED here
);
sub init_tree {
my ($self) = @_;
$self->{tree} = [ {
id => 0,
move => -1,
} ];
}
sub update_tree {
my ($self, $tree) = @_;
my $node = $self->{curnode};
my $up_tree;
my $up_move;
#Carp::cluck KGS::Listener::Debug::dumpval $tree;#d#
#warn "update_tree = ".KGS::Listener::Debug::dumpval $tree;#d#
for (@$tree) {
my ($type, @arg) = @$_;
if ($type eq "add_node") {
$up_tree = 1;
$node = $self->{tree}[$arg[0] + 1]
or die "FATAL: referencing nonexistent node $arg[0]+1!";
my $new = {
id => scalar @{$self->{tree}},
parent => $node->{id},
move => $node->{move} + 1,
};
push @{$self->{tree}}, $new;
push @{$node->{children}}, $new->{id};
$node = $new;
} elsif ($type eq "set_node") {
$node = $self->{tree}[$arg[0] + 1]
or die "set_node to undefined tree node $arg[0]+1";
} elsif ($type eq "set_current") {
$up_tree = 1;
$node = $self->{tree}[$arg[0] + 1]
or die "set_current to undefined tree node $arg[0]+1";
$self->{curnode} = $node;
} elsif ($type eq "mark") {
$up_tree = 1;
my $bit = $arg[1];
my $ref = $node->{"$arg[2],$arg[3]"} ||= [];
$ref->[0] &= ~$exclude_type{$bit};
$ref->[0] |= $bit if $arg[0];
$ref->[1] |= $exclude_type{$bit};
$ref->[2] = $arg[4] if $bit == MARK_LABEL;
} elsif ($type eq "set_stone" or $type eq "move") {
$up_tree = 1;
if ($type eq "move") {
$self->{lastmove_time} = $KGS::Protocol::NOW;
$self->{lastmove_colour} = $arg[0];
$up_move = $arg[1] == 255 if $self->{loaded};
}
if ($arg[1] < 255) {
my $ref = $node->{"$arg[1],$arg[2]"} ||= [];
my $bit = $arg[0] == COLOUR_BLACK ? MARK_B
: $arg[0] == COLOUR_WHITE ? MARK_W
: 0;
$ref->[0] &= ~$exclude_type{$bit || MARK_B};
$ref->[0] |= $bit | ($type eq "move" ? MARK_MOVE : 0);
$ref->[1] |= $exclude_type{$bit || MARK_B};
} else {
warn "PLEASE REPORT: pass coordinates but type is $type" if $type ne "move";#d#
$node->{pass} = 1;
}
} elsif ($type eq "comment") {
if (!defined $arg[0]) {
delete $node->{comment};
} else {
$self->event_update_comments ($node, $arg[0], !exists $node->{comment});
$node->{comment} .= $arg[0];
}
} elsif ($type eq "set_timer") {
$up_tree = 1;#d#
$node->{timer}[$arg[0]] = [$arg[1], $arg[2]];
} elsif ($type eq "score") {
$up_tree = 1;
$node->{score}[$arg[0]] = $arg[1];
} elsif ($type eq "player") {
$node->{player}[$arg[0]] = $arg[1];
} elsif ($type eq "rank") {
$node->{rank}[$arg[0]] = $arg[1];
} elsif ($type eq "more") {
die;
} elsif ($type eq "done") {
die;
$self->{loaded} = 1;
# nop
} else {
$node->{$type} = $arg[0]; # rules, date etc..
$self->event_update_rules ($arg[0]) if $type eq "rules";
}
}
$self->{curnode} = $node;
$self->event_move ($up_move) if defined $up_move;
return $up_tree;
}
sub get_path {
my ($self) = @_;
my @nodes;
my $node = $self->{curnode};
for(;;) {
push @nodes, $node;
last unless $node->{parent};
$node = $self->{tree}[$node->{parent}];
}
[reverse @nodes];
}
sub gen_move_tree {
my ($self, $colour, $x, $y) = @_;
[#d#
#NYI#
[add_node => 0],
];
}
sub event_move { }
sub event_update_tree { }
sub event_update_comments { }
sub event_update_rules { }
1;