DIR : /home/kozerus/public_html/go/dgs/include/move.php
/home/kozerus/public_html/go/dgs/include
<?php
/*
Dragon Go Server
Copyright (C) 2001- Erik Ouchterlony, Rod Ival, Jens-Uwe Gaspar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//$TranslateGroups[] = "Game";
require_once 'include/game_functions.php';
/*!
* \class GameCheckMove
*
* \brief helper-class to handle moving of game.
* \note main-purpose was to avoid globals.
*/
class GameCheckMove
{
private $board;
private $black_prisoners;
private $white_prisoners;
public $prisoners;
public $nr_prisoners;
public $colnr;
public $rownr;
private $replay_last_move = ''; // SGF-coords (used for replaying)
private $replay_game_flags = 0; // GAMEFLAGS_KO
// tracking of last-color/move for handling of cond-moves
private $replay_last_color = BLACK; // BLACK | WHITE
private $replay_last_sgf_move = -1; // SGF-coords
public function __construct( Board &$board )
{
$this->board = $board;
}
public function get_replay_last_color()
{
return $this->replay_last_color;
}
public function get_replay_last_sgf_move()
{
return $this->replay_last_sgf_move;
}
/*! Take over board-array in this GameCheckMove-instance into passed Board-instance. */
public function assign_board_array( Board &$board )
{
$board->array = $this->board->array;
}
/*!
* \brief Checks move on given board adjusting black/white-prisoners.
* \param $coord can be arr(x,y) or one sgf-coord (e.g. 'aa')
* \param $last_move sgf-coord of last-move (e.g. 'aa')
* \return '' (=no error); the following only if $error_exit is false:
* 'illegal_position' (=invalid-coord), 'ko', 'suicide'
*
* \note adjusted globals: $Black_Prisoners, $White_Prisoners
* \note sets $this->prisoners with array of the captured stones of play (or suicided stones if, a day, $suicide_allowed==true)
* \note sets $this->nr_prisoners, colnr/rownr (of given $coord)
*/
public function check_move( $coord, $to_move, $last_move, $game_flags, $error_exit=true )
{
$this->black_prisoners = $this->white_prisoners = $this->nr_prisoners = 0;
$this->prisoners = array();
$Size = $this->board->size;
$array = &$this->board->array;
list($colnr,$rownr) = (is_array($coord)) ? $coord : sgf2number_coords($coord, $Size);
$this->colnr = $colnr;
$this->rownr = $rownr;
if ( !isset($rownr) || !isset($colnr) || @$array[$colnr][$rownr] != NONE )
{
if ( $error_exit )
error('illegal_position', "GCM.check_move({$this->board->gid})");
else
return 'illegal_position';
}
$array[$colnr][$rownr] = $to_move;
$this->board->check_prisoners( $colnr, $rownr, WHITE+BLACK-$to_move, $this->prisoners );
$this->nr_prisoners = count($this->prisoners);
if ( $this->nr_prisoners == 0 )
{
// Check for suicide
$suicide_allowed = false;
if ( !$this->board->has_liberty_check( $colnr, $rownr, $this->prisoners, $suicide_allowed) )
{
if ( !$suicide_allowed )
{
if ( $error_exit )
error('suicide', "GCM.check_move({$this->board->gid})");
else
return 'suicide';
}
}
return ''; // Ok, all tests passed.
}
// note: $game_flags (Games.Flags) has set Ko-flag if last move has taken a single stone
if ( $this->nr_prisoners == 1 && ($game_flags & GAMEFLAGS_KO) )
{
// Check for ko
list($x,$y) = $this->prisoners[0];
if ( $last_move == number2sgf_coords( $x, $y, $Size) )
{
if ( $error_exit )
error('ko', "GCM.check_move({$this->board->gid})");
else
return 'ko';
}
}
if ( $to_move == BLACK )
$this->black_prisoners = $this->nr_prisoners;
else
$this->white_prisoners = $this->nr_prisoners;
return ''; // Ok, all tests passed.
}//check_move
/*! \brief Updates black/white prisoners with diff generated by check_move()-func. */
public function update_prisoners( &$black_pr, &$white_pr )
{
$black_pr += $this->black_prisoners;
$white_pr += $this->white_prisoners;
}//update_prisoners
/*!
* \brief Replays moves on board up to given move-position.
* \param $arr_moves arr( [stone,x,y], ... ) like Board->moves
* \param $last_move_nr last-move that should be played
* \note ignore prisoners-counting
*/
public function replay_moves( array $arr_board_moves, $last_move_nr )
{
$this->init_replay_moves();
foreach ( $arr_board_moves as $move_nr => $arr_move )
{
if ( !is_numeric($move_nr) )
continue;
if ( $move_nr > $last_move_nr )
break;
$err = $this->replay_move( $arr_move );
}
}//replay_moves
public function init_replay_moves()
{
$this->replay_last_move = '';
$this->replay_game_flags = 0;
$this->replay_last_color = BLACK;
$this->replay_last_sgf_move = -1;
}
/*!
* \brief Replays single move on board (PASS-move is skipped).
* \param $move array( BLACK|WHITE, x, y ) with x=POSX_PASS for PASS-move;
* or sgf-coord "<COL><SGF_COORD|''>" e.g. 'Baa', 'W'
* \return error-string from this->check_move(); or else '' on successfully played move
*/
public function replay_move( $move )
{
static $MAP_COLORS = array( 'B' => BLACK, 'W' => WHITE );
if ( is_array($move) )
{
list( $stone, $x, $y ) = $move;
$sgf_move = ( $x <= POSX_PASS ) ? '' : number2sgf_coords( $x, $y, $this->board->size );
}
else
{
$stone = @$MAP_COLORS[$move[0]];
$sgf_move = substr($move, 1);
if ( $sgf_move )
list( $x, $y ) = sgf2number_coords( $sgf_move, $this->board->size );
else
$x = POSX_PASS;
}
if ( $x < POSX_PASS || ($stone != BLACK && $stone != WHITE) )
$err = ''; // keep last-move/game-flags for non-move
elseif ( $x == POSX_PASS ) // PASS
{
$err = ''; // keep last-move/game-flags on PASS-move for ko-check
$this->replay_last_color = $stone;
$this->replay_last_sgf_move = '';
}
else // move
{
$err = $this->check_move( array($x,$y), $stone, $this->replay_last_move, $this->replay_game_flags, /*exit*/false );
if ( !$err )
{
if ( $this->nr_prisoners == 1 )
$this->replay_game_flags |= GAMEFLAGS_KO;
else
$this->replay_game_flags &= ~GAMEFLAGS_KO;
$this->replay_last_move = $sgf_move;
$this->replay_last_color = $stone;
$this->replay_last_sgf_move = $sgf_move;
}
}
return $err;
}//replay_move
public function __clone()
{
$this->board = clone $this->board;
}
// ------------ static functions ----------------------------
/*! \brief Prepares a new GameCheckMove-instance with given board-size and initial shape-setup. */
public static function prepare_game_check_move_board_start( $gid, $size, $shape_snapshot )
{
$board = new Board( $gid, $size ); // need board to really "move" (with capturing stones)
$board->init_board();
if ( $shape_snapshot )
{
$arr_xy = GameSnapshot::parse_stones_snapshot( $size, $shape_snapshot, BLACK, WHITE );
if ( count($arr_xy) )
{
foreach ( $arr_xy as $arr_setup )
{
list( $Stone, $PosX, $PosY ) = $arr_setup;
$board->array[$PosX][$PosY] = $Stone;
}
}
}
$gchkmove = new GameCheckMove( $board );
return $gchkmove;
}//prepare_game_check_move_board_start
} // end 'GameCheckMove'
/*!
* \brief Places handicap stones adjusted board-array.
* \param $board Board-object, is modified with setting black handicap stones
* \param $stonestring coordinate-list of handicap-stones to place
* \param $coord if != false contains SGF-coord to add black-stone to stone-string
* \return new list of strone-string with coords of stones
*/
function check_handicap( Board &$board, $stonestring, $coord = false )
{
$Size= $board->size;
$array= &$board->array;
// add handicap stones to array
$l = strlen( $stonestring );
for ( $i=0; $i < $l; $i += 2 )
{
list($colnr,$rownr) = sgf2number_coords(substr($stonestring, $i, 2), $Size);
if ( !isset($rownr) || !isset($colnr) || @$array[$colnr][$rownr] != NONE )
error('illegal_position', "check_handicap.1({$board->gid},$colnr,$rownr,$stonestring)");
$array[$colnr][$rownr] = BLACK;
}
if ( $coord )
{
list($colnr,$rownr) = sgf2number_coords($coord, $Size);
if ( !isset($rownr) || !isset($colnr) || @$array[$colnr][$rownr] != NONE )
error('illegal_position', "check_handicap.2({$board->gid},$colnr,$rownr,$stonestring)");
$array[$colnr][$rownr] = BLACK;
$stonestring .= $coord;
}
return $stonestring;
}//check_handicap
/*!
* \class GameCheckScore
*
* \brief helper-class to handle scoring of game.
* \note main-purpose was to avoid globals.
*/
class GameCheckScore
{
private $board;
private $stonestring;
private $handicap;
private $komi;
private $black_prisoners;
private $white_prisoners;
private $toggle_unique = false;
public function __construct( Board &$board, $stonestring, $handicap, $komi, $black_prisoners, $white_prisoners )
{
$this->board = $board;
$this->stonestring = (@$stonestring) ? $stonestring : '';
$this->handicap = $handicap;
$this->komi = $komi;
$this->black_prisoners = $black_prisoners;
$this->white_prisoners = $white_prisoners;
}
public function set_toggle_unique()
{
$this->toggle_unique = true;
}
/*!
* \brief Checks removal of stones by toggling stones.
* \param $ruleset RULESET_...
* \param $coord false to just treat stonestring; single sgf-coord or array of sgf-coords to toggle
* \return GameScore-object
*/
public function check_remove( $ruleset, $coords=false, $with_board_status=false )
{
$Size = $this->board->size;
$array =& $this->board->array;
// toggle marked stones and marked dame to array
// $stonearray is used to cancel out duplicates, in order to make $stonestring_loc shorter.
$stonearray = array();
$l = strlen($this->stonestring);
for ( $i=0; $i < $l; $i += 2 )
{
list($colnr,$rownr) = sgf2number_coords(substr($this->stonestring, $i, 2), $Size);
if ( !isset($rownr) || !isset($colnr) )
error('illegal_position', "GCS.check_remove.move4($colnr,$rownr)");
$stone = isset($array[$colnr][$rownr]) ? $array[$colnr][$rownr] : NONE ;
if ( $stone == BLACK || $stone == WHITE || $stone == NONE ) //NONE for MARKED_DAME
$array[$colnr][$rownr] = $stone + OFFSET_MARKED;
else if ( $stone == BLACK_DEAD || $stone == WHITE_DEAD || $stone == MARKED_DAME )
$array[$colnr][$rownr] = $stone - OFFSET_MARKED;
if ( !isset($stonearray[$colnr][$rownr]) )
$stonearray[$colnr][$rownr] = true;
else
unset( $stonearray[$colnr][$rownr] );
}
if ( $coords )
{
if ( !is_array($coords) )
$coords = array( $coords );
$toggled = ( $this->toggle_unique ) ? array() : NULL;
foreach ( $coords as $coord )
{
list($colnr,$rownr) = sgf2number_coords($coord, $Size);
if ( !isset($rownr) || !isset($colnr) )
error('illegal_position', "GCS.check_remove.move5($colnr,$rownr)");
$stone = isset($array[$colnr][$rownr]) ? $array[$colnr][$rownr] : NONE ;
if ( MAX_SEKI_MARK<=0 || ($stone!=NONE && $stone!=MARKED_DAME) )
{
if ( $stone!=BLACK && $stone!=WHITE && $stone!=BLACK_DEAD && $stone!=WHITE_DEAD )
error('illegal_position', "GCS.check_remove.move6($colnr,$rownr,$stone)");
}
$marked = array();
$this->board->toggle_marked_area( $colnr, $rownr, $marked, $toggled );
foreach ( $marked as $sub )
{
list($colnr,$rownr) = $sub;
if ( !isset( $stonearray[$colnr][$rownr] ) )
$stonearray[$colnr][$rownr] = true;
else
unset( $stonearray[$colnr][$rownr] );
}
}
}
$this->stonestring = '';
foreach ( $stonearray as $colnr => $sub )
{
foreach ( $sub as $rownr => $dummy )
$this->stonestring .= number2sgf_coords($colnr, $rownr, $Size);
}
$game_score = new GameScore( $ruleset, $this->handicap, $this->komi );
$game_score->set_prisoners_all( $this->black_prisoners, $this->white_prisoners );
$this->board->fill_game_score( $game_score, /*coords*/false, $with_board_status );
return $game_score;
}//check_remove
/*! \brief Update stonestring after check_remove()-call. */
public function update_stonestring( &$stonestr )
{
$stonestr = $this->stonestring;
}
} // end 'GameCheckScore'
?>
//
koh5_pano
Drag mouse to navigate.
Navigation
- Left/Right Mouse drag: Changes camera heading.
- Up/Sown Mouse drag: Changes camera pitch.
- Scroll wheel: Changes camera field of view.
- I-Key: Displays Info panel with canvas size, image size and FPS.
17.Aug.2010, Martin Wengenmayer