Checking The Win ConditionΒΆ
Lastly, we need to check if the game has ended and declare a winner if so.
A game can end in one of two ways:
- A player has three in a row (row, column, or diagonal) and wins
- All positions on the board have been filled and no player fulfills the winning condition, resulting in a draw
To check for wins, we need to iterate over the rows, columns, and diagonals and see if all tokens belong to the same player:
# check a row, column, or diagonal ($delta specifies the direction)
DEF check_group $start_position $delta:
DEBUG CONCAT CONCAT "check_group: " STR $start_position STR $delta;
ASSIGN 'x_ct' 0;
ASSIGN 'o_ct' 0;
ASSIGN 'k' 0;
ASSIGN 'pos' $start_position;
WHILE LESS $k 3:
ASSIGN 'token' GET_BY_POSITION GET_CONTAINER CONCAT STR GET_POSITION_ELT $pos 0 STR GET_POSITION_ELT $pos 1 0;
ASSIGN 'token_type' NULL;
IF NOT IS_NULL $token:
ASSIGN 'token_type' TOKEN_GET_PROP $token 'token_type';
END_IF
DEBUG $token_type;
DEBUG $token;
IF EQUAL $token_type 'x':
ASSIGN 'x_ct' PLUS $x_ct 1;
END_IF
IF EQUAL $token_type 'o':
ASSIGN 'o_ct' PLUS $o_ct 1;
END_IF
ASSIGN 'k' PLUS $k 1;
ASSIGN 'pos' TUPLE2
PLUS GET_POSITION_ELT $pos 0 GET_POSITION_ELT $delta 0
PLUS GET_POSITION_ELT $pos 1 GET_POSITION_ELT $delta 1;
END_WHILE
DEBUG STR TUPLE2 $x_ct $o_ct;
IF EQUAL $x_ct 3:
RETURN 'x';
END_IF
IF EQUAL $o_ct 3:
RETURN 'o';
END_IF
RETURN NULL;
END_DEF
DEF get_winner:
DEBUG "computing winner";
ASSIGN 'i' 0;
WHILE LESS $i 3:
# check row
ASSIGN 'tmp' check_group TUPLE2 $i 0 TUPLE2 0 1;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
# check column
ASSIGN 'tmp' check_group TUPLE2 0 $i TUPLE2 1 0;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
ASSIGN 'i' PLUS $i 1;
END_WHILE
# check diagonals
ASSIGN 'tmp' check_group TUPLE2 0 0 TUPLE2 1 1;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
ASSIGN 'tmp' check_group TUPLE2 0 2 TUPLE2 1 -1;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
END_DEF
To check if all positions have been filled and combine this result with the preceding:
DEF all_tokens_placed:
ASSIGN 'i' 0;
WHILE LESS $i 3:
ASSIGN 'j' 0;
WHILE LESS $j 3:
IF EQUAL SIZE GET_CONTAINER CONCAT STR $i STR $j 0:
RETURN FALSE;
END_IF
ASSIGN 'j' PLUS $j 1;
END_WHILE
ASSIGN 'i' PLUS $i 1;
END_WHILE
RETURN TRUE;
END_DEF
DEF get_winner:
DEBUG "computing winner";
ASSIGN 'i' 0;
WHILE LESS $i 3:
ASSIGN 'tmp' check_group TUPLE2 $i 0 TUPLE2 0 1;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
ASSIGN 'tmp' check_group TUPLE2 0 $i TUPLE2 1 0;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
ASSIGN 'i' PLUS $i 1;
END_WHILE
ASSIGN 'tmp' check_group TUPLE2 0 0 TUPLE2 1 1;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
ASSIGN 'tmp' check_group TUPLE2 0 2 TUPLE2 1 -1;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
END_DEF
DEF check_game_end:
ASSIGN 'winner' get_winner;
IF OR
all_tokens_placed
NOT IS_NULL $winner
:
SET_PROP "game_over" TRUE;
IF IS_NULL $winner:
SET_PROP 'winner' 'DRAW';
RETURN NULL;
END_IF
SET_PROP 'winner' $winner;
RETURN NULL;
END_IF
END_DEF
We’ll also need to actually invoke the check_game_end
function from the receiver, so:
RECEIVE place_token $position:
...
IF EQUAL SIZE $target 0:
APPEND $target POP $token_source 0;
ROTATE_PLAY_ORDER;
# ADD THIS vvv
check_game_end;
ACCEPT;
END_IF
...
END_RECEIVE
Putting it all together, the complete ruleset looks like this:
DEF set_up:
INSTANTIATE_TOKEN 12 "x" 5 "x_tokens";
INSTANTIATE_TOKEN 12 "o" 4 "o_tokens";
END_DEF
DEF all_tokens_placed:
ASSIGN 'i' 0;
WHILE LESS $i 3:
ASSIGN 'j' 0;
WHILE LESS $j 3:
IF EQUAL SIZE GET_CONTAINER CONCAT STR $i STR $j 0:
RETURN FALSE;
END_IF
ASSIGN 'j' PLUS $j 1;
END_WHILE
ASSIGN 'i' PLUS $i 1;
END_WHILE
RETURN TRUE;
END_DEF
DEF check_group $start_position $delta:
DEBUG CONCAT CONCAT "check_group: " STR $start_position STR $delta;
ASSIGN 'x_ct' 0;
ASSIGN 'o_ct' 0;
ASSIGN 'k' 0;
ASSIGN 'pos' $start_position;
WHILE LESS $k 3:
ASSIGN 'token' GET_BY_POSITION GET_CONTAINER CONCAT STR GET_POSITION_ELT $pos 0 STR GET_POSITION_ELT $pos 1 0;
ASSIGN 'token_type' NULL;
IF NOT IS_NULL $token:
ASSIGN 'token_type' TOKEN_GET_PROP $token 'token_type';
END_IF
DEBUG $token_type;
DEBUG $token;
IF EQUAL $token_type 'x':
ASSIGN 'x_ct' PLUS $x_ct 1;
END_IF
IF EQUAL $token_type 'o':
ASSIGN 'o_ct' PLUS $o_ct 1;
END_IF
ASSIGN 'k' PLUS $k 1;
ASSIGN 'pos' TUPLE2
PLUS GET_POSITION_ELT $pos 0 GET_POSITION_ELT $delta 0
PLUS GET_POSITION_ELT $pos 1 GET_POSITION_ELT $delta 1;
END_WHILE
DEBUG STR TUPLE2 $x_ct $o_ct;
IF EQUAL $x_ct 3:
RETURN 'x';
END_IF
IF EQUAL $o_ct 3:
RETURN 'o';
END_IF
RETURN NULL;
END_DEF
DEF get_winner:
DEBUG "computing winner";
ASSIGN 'i' 0;
WHILE LESS $i 3:
ASSIGN 'tmp' check_group TUPLE2 $i 0 TUPLE2 0 1;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
ASSIGN 'tmp' check_group TUPLE2 0 $i TUPLE2 1 0;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
ASSIGN 'i' PLUS $i 1;
END_WHILE
ASSIGN 'tmp' check_group TUPLE2 0 0 TUPLE2 1 1;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
ASSIGN 'tmp' check_group TUPLE2 0 2 TUPLE2 1 -1;
IF NOT IS_NULL $tmp:
RETURN $tmp;
END_IF
END_DEF
DEF check_game_end:
ASSIGN 'winner' get_winner;
IF OR
all_tokens_placed
NOT IS_NULL $winner
:
SET_PROP "game_over" TRUE;
IF IS_NULL $winner:
SET_PROP 'winner' 'DRAW';
RETURN NULL;
END_IF
SET_PROP 'winner' $winner;
RETURN NULL;
END_IF
END_DEF
RECEIVE place_token $position:
IF NOT EQUAL $PLAYER_NAME $CURRENT_PLAYER:
REJECT "Not your turn";
END_IF
ASSIGN 'target' GET_CONTAINER $position;
ASSIGN 'token_source' GET_CONTAINER CONCAT $CURRENT_PLAYER "_tokens";
IF EQUAL SIZE $target 0:
APPEND $target POP $token_source 0;
ROTATE_PLAY_ORDER;
check_game_end;
ACCEPT;
END_IF
REJECT "Can't play there (non-empty)";
END_RECEIVE