Just another update
This commit is contained in:
parent
a25938f123
commit
00a190b0a6
6591 changed files with 94363 additions and 23227 deletions
|
|
@ -1 +1,2 @@
|
|||
Play a game of [[wp:Tic-tac-toe|tic-tac-toe]]. Ensure that legal moves are played and that a winning position is notified.
|
||||
Play a game of [[wp:Tic-tac-toe|tic-tac-toe]].
|
||||
Ensure that legal moves are played and that a winning position is notified.
|
||||
|
|
|
|||
92
Task/Tic-tac-toe/Common-Lisp/tic-tac-toe.lisp
Normal file
92
Task/Tic-tac-toe/Common-Lisp/tic-tac-toe.lisp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
(defun generate-board ()
|
||||
(loop repeat 9 collect nil))
|
||||
|
||||
(defparameter *straights* '((1 2 3) (4 5 6) (7 8 9) (1 4 7) (2 5 8) (3 6 9) (1 5 9) (3 5 7)))
|
||||
(defparameter *current-player* 'x)
|
||||
|
||||
(defun get-board-elt (n board)
|
||||
(nth (1- n) board))
|
||||
|
||||
(defun legal-p (n board)
|
||||
(null (get-board-elt n board)))
|
||||
|
||||
(defun set-board-elt (n board symbol)
|
||||
(if (legal-p n board)
|
||||
(setf (nth (1- n) board) symbol)
|
||||
(progn (format t "Illegal move. Try again.~&")
|
||||
(set-board-elt (read) board symbol))))
|
||||
|
||||
(defun list-legal-moves (board)
|
||||
(loop for i from 1 to (length board)
|
||||
when (legal-p i board)
|
||||
collect i))
|
||||
|
||||
(defun get-random-element (lst)
|
||||
(nth (random (length lst)) lst))
|
||||
|
||||
(defun multi-non-nil-eq (lst)
|
||||
(and (notany #'null lst)
|
||||
(notany #'null (mapcar #'(lambda (x) (eq (car lst) x)) lst))
|
||||
(car lst)))
|
||||
|
||||
(defun elements-of-straights (board)
|
||||
(loop for i in *straights*
|
||||
collect (loop for j from 0 to 2
|
||||
collect (get-board-elt (nth j i) board))))
|
||||
|
||||
(defun find-winner (board)
|
||||
(car (remove-if #'null (mapcar #'multi-non-nil-eq (elements-of-straights board)))))
|
||||
|
||||
(defun set-player (mark)
|
||||
(format t "Shall a computer play as ~a? (y/n)~&" mark)
|
||||
(let ((response (read)))
|
||||
(cond ((equalp response 'y) t)
|
||||
((equalp response 'n) nil)
|
||||
(t (format t "Come again?~&")
|
||||
(set-player mark)))))
|
||||
|
||||
(defun player-move (board symbol)
|
||||
(format t "~%Player ~a, please input your move.~&" symbol)
|
||||
(set-board-elt (read) board symbol)
|
||||
(format t "~%"))
|
||||
|
||||
(defun computer-move (board symbol)
|
||||
(let ((move (get-random-element (list-legal-moves board))))
|
||||
(set-board-elt move board symbol)
|
||||
(format t "~%computer selects ~a~%~%" move)))
|
||||
|
||||
(defun computer-move-p (current-player autoplay-x-p autoplay-o-p)
|
||||
(if (eq current-player 'x)
|
||||
autoplay-x-p
|
||||
autoplay-o-p))
|
||||
|
||||
(defun perform-turn (current-player board autoplay-x-p autoplay-o-p)
|
||||
(if (computer-move-p current-player autoplay-x-p autoplay-o-p)
|
||||
(computer-move board current-player)
|
||||
(player-move board current-player)))
|
||||
|
||||
(defun switch-player ()
|
||||
(if (eq *current-player* 'x)
|
||||
(setf *current-player* 'o)
|
||||
(setf *current-player* 'x)))
|
||||
|
||||
(defun display-board (board)
|
||||
(loop for i downfrom 2 to 0
|
||||
do (loop for j from 1 to 3
|
||||
initially (format t "|")
|
||||
do (format t "~a|" (or (get-board-elt (+ (* 3 i) j) board) (+ (* 3 i) j)))
|
||||
finally (format t "~&"))))
|
||||
|
||||
(defun tic-tac-toe ()
|
||||
(setf *current-player* 'x)
|
||||
(let ((board (generate-board))
|
||||
(autoplay-x-p (set-player 'x))
|
||||
(autoplay-o-p (set-player 'o)))
|
||||
(format t "~%")
|
||||
(loop until (or (find-winner board) (null (list-legal-moves board)))
|
||||
do (display-board board)
|
||||
do (perform-turn *current-player* board autoplay-x-p autoplay-o-p)
|
||||
do (switch-player)
|
||||
finally (if (find-winner board)
|
||||
(format t "The winner is ~a!" (find-winner board))
|
||||
(format t "It's a tie.")))))
|
||||
|
|
@ -6,7 +6,7 @@ struct GameBoard {
|
|||
enum : dchar { human = 'X', computer = 'O' }
|
||||
enum Game { going, humanWins, computerWins, draw }
|
||||
|
||||
const pure nothrow invariant() {
|
||||
const pure nothrow @safe @nogc invariant() {
|
||||
int nHuman = 0, nComputer = 0;
|
||||
foreach (immutable i, immutable c; board)
|
||||
if (c.isDigit)
|
||||
|
|
@ -22,15 +22,15 @@ struct GameBoard {
|
|||
return format("%(%-(%s|%)\n-+-+-\n%)", board[].chunks(3));
|
||||
}
|
||||
|
||||
bool isAvailable(in int i) const pure nothrow {
|
||||
bool isAvailable(in int i) const pure nothrow @safe @nogc {
|
||||
return i >= 0 && i < 9 && board[i].isDigit;
|
||||
}
|
||||
|
||||
int[] availablePositions() const pure nothrow {
|
||||
return 9.iota.filter!(i => isAvailable(i)).array;
|
||||
auto availablePositions() const pure nothrow @safe /*@nogc*/ {
|
||||
return 9.iota.filter!(i => isAvailable(i));
|
||||
}
|
||||
|
||||
Game winner() const pure nothrow {
|
||||
Game winner() const pure nothrow @safe /*@nogc*/ {
|
||||
static immutable wins = [[0, 1, 2], [3, 4, 5], [6, 7, 8],
|
||||
[0, 3, 6], [1, 4, 7], [2, 5, 8],
|
||||
[0, 4, 8], [2, 4, 6]];
|
||||
|
|
@ -49,7 +49,7 @@ struct GameBoard {
|
|||
return availablePositions.empty ? Game.draw: Game.going;
|
||||
}
|
||||
|
||||
bool isFinished() const pure nothrow {
|
||||
bool isFinished() const pure nothrow @safe /*@nogc*/ {
|
||||
return winner != Game.going;
|
||||
}
|
||||
|
||||
|
|
@ -57,8 +57,8 @@ struct GameBoard {
|
|||
out(res) {
|
||||
assert(res >= 0 && res < 9 && isAvailable(res));
|
||||
} body {
|
||||
// return availablePositions.choice;
|
||||
return availablePositions[uniform(0, $)];
|
||||
// return availablePositions.array.choice;
|
||||
return availablePositions.array[uniform(0, $)];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
100
Task/Tic-tac-toe/Euphoria/tic-tac-toe.euphoria
Normal file
100
Task/Tic-tac-toe/Euphoria/tic-tac-toe.euphoria
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
include std/console.e
|
||||
include std/text.e
|
||||
include std/search.e
|
||||
include std/sequence.e
|
||||
|
||||
sequence board
|
||||
sequence players = {"X","O"}
|
||||
|
||||
function DisplayBoard()
|
||||
for i = 1 to 3 do
|
||||
for j = 1 to 3 do
|
||||
printf(1,"%s",board[i][j])
|
||||
if j < 3 then
|
||||
printf(1,"%s","|")
|
||||
end if
|
||||
end for
|
||||
if i < 3 then
|
||||
puts(1,"\n-----\n")
|
||||
else
|
||||
puts(1,"\n\n")
|
||||
end if
|
||||
end for
|
||||
|
||||
return 1
|
||||
end function
|
||||
|
||||
function CheckWinner()
|
||||
sequence temp = board
|
||||
for a = 1 to 2 do
|
||||
for i = 1 to 3 do
|
||||
if equal({"X","X","X"},temp[i]) then
|
||||
puts(1,"X wins\n\n")
|
||||
return 1
|
||||
elsif equal({"O","O","O"},temp[i]) then
|
||||
puts(1,"O wins\n\n")
|
||||
return 1
|
||||
end if
|
||||
end for
|
||||
temp = columnize(board)
|
||||
end for
|
||||
if equal({"X","X","X"},{board[1][1],board[2][2],board[3][3]}) or
|
||||
equal({"X","X","X"},{board[1][3],board[2][2],board[3][1]}) then
|
||||
puts(1,"X wins\n")
|
||||
return 1
|
||||
elsif equal({"O","O","O"},{board[1][1],board[2][2],board[3][3]}) or
|
||||
equal({"O","O","O"},{board[1][3],board[2][2],board[3][1]}) then
|
||||
puts(1,"O wins\n")
|
||||
return 1
|
||||
end if
|
||||
|
||||
if moves = 9 then
|
||||
puts(1,"Draw\n\n")
|
||||
return 1
|
||||
end if
|
||||
|
||||
return 0
|
||||
end function
|
||||
|
||||
integer turn, row, column, moves
|
||||
sequence replay
|
||||
while 1 do
|
||||
board = repeat(repeat(" ",3),3)
|
||||
DisplayBoard()
|
||||
turn = rand(2)
|
||||
moves = 0
|
||||
|
||||
while 1 do
|
||||
while 1 do
|
||||
printf(1,"%s's turn\n",players[turn])
|
||||
row = prompt_number("Enter row: ",{})
|
||||
column = prompt_number("Enter column: ",{})
|
||||
if match(board[row][column]," ") then
|
||||
board[row][column] = players[turn]
|
||||
moves += 1
|
||||
exit
|
||||
else
|
||||
puts(1,"Space already taken - pick again\n")
|
||||
end if
|
||||
end while
|
||||
|
||||
DisplayBoard()
|
||||
|
||||
if CheckWinner() then
|
||||
exit
|
||||
end if
|
||||
|
||||
if turn = 1 then
|
||||
turn = 2
|
||||
else
|
||||
turn = 1
|
||||
end if
|
||||
end while
|
||||
|
||||
replay = lower(prompt_string("Play again (y/n)?\n\n"))
|
||||
|
||||
if match(replay,"n") then
|
||||
exit
|
||||
end if
|
||||
|
||||
end while
|
||||
|
|
@ -147,7 +147,7 @@ changePlayer 'X' = 'O'
|
|||
-- first, the computer looks for two pieces of his opponent in a row
|
||||
-- and tries to block.
|
||||
-- otherwise, it tries to guess the best position for the next movement.
|
||||
-- as a least ressource, it places a piece randomly.
|
||||
-- as a last resort, it places a piece randomly.
|
||||
autoTurn :: Bool -> (Int, Char, String) -> IO (Int, Char, String)
|
||||
autoTurn forceRandom (count, player, game) = do
|
||||
-- try a random position 'cause everything else failed
|
||||
|
|
|
|||
224
Task/Tic-tac-toe/MATLAB/tic-tac-toe.m
Normal file
224
Task/Tic-tac-toe/MATLAB/tic-tac-toe.m
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
function TicTacToe
|
||||
|
||||
% Set up the board (one for each player)
|
||||
boards = false(3, 3, 2); % Players' pieces
|
||||
rep = [' 1 | 4 | 7' ; ' 2 | 5 | 8' ; ' 3 | 6 | 9'];
|
||||
|
||||
% Prompt user with options
|
||||
fprintf('Welcome to Tic-Tac-Toe!\n')
|
||||
nHumans = str2double(input('Enter the number of human players: ', 's'));
|
||||
if isnan(nHumans) || ceil(nHumans) ~= nHumans || nHumans < 1 || nHumans > 2
|
||||
nHumans = 0;
|
||||
pHuman = false(2, 1);
|
||||
elseif nHumans == 1
|
||||
humanFirst = input('Would the human like to go first (Y/N)? ', 's');
|
||||
if length(humanFirst) == 1 && lower(humanFirst) == 'n'
|
||||
pHuman = [false ; true];
|
||||
else
|
||||
pHuman = [true ; false];
|
||||
end
|
||||
else
|
||||
pHuman = true(2, 1);
|
||||
end
|
||||
if any('o' == input('Should Player 1 use X or O? ', 's'))
|
||||
marks = 'OX';
|
||||
else
|
||||
marks = 'XO';
|
||||
end
|
||||
fprintf('So Player 1 is %shuman and %cs and Player 2 is %shuman and %cs.\n', ...
|
||||
char('not '.*~pHuman(1)), marks(1), char('not '.*~pHuman(2)), marks(2))
|
||||
if nHumans > 0
|
||||
fprintf('Select the space to mark by entering the space number.\n')
|
||||
fprintf('No entry will quit the game.\n')
|
||||
end
|
||||
|
||||
% Play game
|
||||
gameOver = false;
|
||||
turn = 1;
|
||||
while ~gameOver
|
||||
fprintf('\n')
|
||||
disp(rep)
|
||||
fprintf('\n')
|
||||
if pHuman(turn)
|
||||
[move, isValid, isQuit] = GetMoveFromPlayer(turn, boards);
|
||||
gameOver = isQuit;
|
||||
else
|
||||
move = GetMoveFromComputer(turn, boards);
|
||||
fprintf('Player %d chooses %d\n', turn, move)
|
||||
isValid = true;
|
||||
isQuit = false;
|
||||
end
|
||||
if isValid && ~isQuit
|
||||
[r, c] = ind2sub([3 3], move);
|
||||
boards(r, c, turn) = true;
|
||||
rep(r, 4*c) = marks(turn);
|
||||
if CheckWin(boards(:, :, turn))
|
||||
gameOver = true;
|
||||
fprintf('\n')
|
||||
disp(rep)
|
||||
fprintf('\nPlayer %d wins!\n', turn)
|
||||
elseif CheckDraw(boards)
|
||||
gameOver = true;
|
||||
fprintf('\n')
|
||||
disp(rep)
|
||||
fprintf('\nCat''s game!\n')
|
||||
end
|
||||
turn = ~(turn-1)+1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function [move, isValid, isQuit] = GetMoveFromPlayer(pNum, boards)
|
||||
% move - 1-9 indicating move position, 0 if invalid move
|
||||
% isValid - logical indicating if move was valid, true if quitting
|
||||
% isQuit - logical indicating if player wishes to quit game
|
||||
p1 = boards(:, :, 1);
|
||||
p2 = boards(:, :, 2);
|
||||
moveStr = input(sprintf('Player %d: ', pNum), 's');
|
||||
if isempty(moveStr)
|
||||
fprintf('Play again soon!\n')
|
||||
move = 0;
|
||||
isValid = true;
|
||||
isQuit = true;
|
||||
else
|
||||
move = str2double(moveStr);
|
||||
isQuit = false;
|
||||
if isnan(move) || move < 1 || move > 9 || p1(move) || p2(move)
|
||||
fprintf('%s is an invalid move.\n', moveStr)
|
||||
isQuit = 0;
|
||||
isValid = false;
|
||||
else
|
||||
isValid = true;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function move = GetMoveFromComputer(pNum, boards)
|
||||
% pNum - 1-2 player number
|
||||
% boards - 3x3x2 logical array where pBoards(:,:,1) is player 1's marks
|
||||
% Assumes that it is possible to make a move
|
||||
if ~any(boards(:)) % Play in the corner for first move
|
||||
move = 1;
|
||||
else % Use Newell and Simon's "rules to win"
|
||||
pMe = boards(:, :, pNum);
|
||||
pThem = boards(:, :, ~(pNum-1)+1);
|
||||
possMoves = find(~(pMe | pThem)).';
|
||||
|
||||
% Look for a winning move
|
||||
move = FindWin(pMe, possMoves);
|
||||
if move
|
||||
return
|
||||
end
|
||||
|
||||
% Look to block opponent from winning
|
||||
move = FindWin(pThem, possMoves);
|
||||
if move
|
||||
return
|
||||
end
|
||||
|
||||
% Look to create a fork (two non-blocked lines of two)
|
||||
for m = possMoves
|
||||
newPMe = pMe;
|
||||
newPMe(m) = true;
|
||||
if CheckFork(newPMe, pThem)
|
||||
move = m;
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
% Look to make two in a row so long as it doesn't force opponent to fork
|
||||
notGoodMoves = false(size(possMoves));
|
||||
for m = possMoves
|
||||
newPMe = pMe;
|
||||
newPMe(m) = true;
|
||||
if CheckPair(newPMe, pThem)
|
||||
nextPossMoves = possMoves;
|
||||
nextPossMoves(nextPossMoves == m) = [];
|
||||
theirMove = FindWin(newPMe, nextPossMoves);
|
||||
newPThem = pThem;
|
||||
newPThem(theirMove) = true;
|
||||
if ~CheckFork(newPThem, newPMe)
|
||||
move = m;
|
||||
return
|
||||
else
|
||||
notGoodMoves(possMoves == m) = true;
|
||||
end
|
||||
end
|
||||
end
|
||||
possMoves(notGoodMoves) = [];
|
||||
|
||||
% Play the center if available
|
||||
if any(possMoves == 5)
|
||||
move = 5;
|
||||
return
|
||||
end
|
||||
|
||||
% Play the opposite corner of the opponent's piece if available
|
||||
corners = [1 3 7 9];
|
||||
move = intersect(possMoves, ...
|
||||
corners(~(pMe(corners) | pThem(corners)) & pThem(fliplr(corners))));
|
||||
if ~isempty(move)
|
||||
move = move(1);
|
||||
return
|
||||
end
|
||||
|
||||
% Play an empty corner if available
|
||||
move = intersect(possMoves, corners);
|
||||
if move
|
||||
move = move(1);
|
||||
return
|
||||
end
|
||||
|
||||
% Play an empty side if available
|
||||
sides = [2 4 6 8];
|
||||
move = intersect(possMoves, sides);
|
||||
if move
|
||||
move = move(1);
|
||||
return
|
||||
end
|
||||
|
||||
% No good moves, so move randomly
|
||||
possMoves = find(~(pMe | pThem));
|
||||
move = possMoves(randi(length(possMoves)));
|
||||
end
|
||||
end
|
||||
|
||||
function move = FindWin(board, possMoves)
|
||||
% board - 3x3 logical representing one player's pieces
|
||||
% move - integer indicating position to move to win, or 0 if no winning move
|
||||
for m = possMoves
|
||||
newPMe = board;
|
||||
newPMe(m) = true;
|
||||
if CheckWin(newPMe)
|
||||
move = m;
|
||||
return
|
||||
end
|
||||
end
|
||||
move = 0;
|
||||
end
|
||||
|
||||
function win = CheckWin(board)
|
||||
% board - 3x3 logical representing one player's pieces
|
||||
% win - logical indicating if that player has a winning board
|
||||
win = any(all(board)) || any(all(board, 2)) || ...
|
||||
all(diag(board)) || all(diag(fliplr(board)));
|
||||
end
|
||||
|
||||
function fork = CheckFork(p1, p2)
|
||||
% fork - logical indicating if player 1 has created a fork unblocked by player 2
|
||||
fork = sum([sum(p1)-sum(p2) (sum(p1, 2)-sum(p2, 2)).' ...
|
||||
sum(diag(p1))-sum(diag(p2)) ...
|
||||
sum(diag(fliplr(p1)))-sum(diag(fliplr(p2)))] == 2) > 1;
|
||||
end
|
||||
|
||||
function pair = CheckPair(p1, p2)
|
||||
% pair - logical indicating if player 1 has two in a line unblocked by player 2
|
||||
pair = any([sum(p1)-sum(p2) (sum(p1, 2)-sum(p2, 2)).' ...
|
||||
sum(diag(p1))-sum(diag(p2)) ...
|
||||
sum(diag(fliplr(p1)))-sum(diag(fliplr(p2)))] == 2);
|
||||
end
|
||||
|
||||
function draw = CheckDraw(boards)
|
||||
% boards - 3x3x2 logical representation of all players' pieces
|
||||
draw = all(all(boards(:, :, 1) | boards(:, :, 2)));
|
||||
end
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
oops =$ '***error!*** '; cell# ='cell number' /*a couple of literals*/
|
||||
$=copies('─',9) /*eyecatcher literal for messages*/
|
||||
sing='│─┼'; jam='║'; bar='═'; junc='╬'; dbl=jam || bar || junc
|
||||
sw=80-1 /*LINESIZE() bif would be better.*/
|
||||
sw=linesize()-1 /*get the width of the terminal. */
|
||||
parse arg N hm cm .,@.; if N=='' then N=3; oN=N /*specifying some args?*/
|
||||
N=abs(N); NN=N*N; middle=NN%2+N%2 /*if N < 0, computer goes first.*/
|
||||
if N<2 then do; say oops 'tic-tac-toe grid is too small: ' N; exit; end
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
;; optimal-move :: State -> Move
|
||||
;; Choses the optimal move.
|
||||
;; If several equipollent moves exist -- choses one randomly.
|
||||
;; If several equivalent moves exist -- choses one randomly.
|
||||
(define/public ((optimal-move look-ahead) S)
|
||||
(! (argmax (λ (m) (! (minimax (game-tree S m look-ahead))))
|
||||
(shuffle (possible-moves S)))))
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
[i 1]
|
||||
[s (my-move S m)])
|
||||
(cond
|
||||
[(my-win? s) (/ 1 i)] ; more close wins and looses
|
||||
[(my-win? s) (/ 1 i)] ; more close wins and loses
|
||||
[(my-loss? s) (/ -1 i)] ; have bigger weights
|
||||
[(draw-game? s) 0]
|
||||
[(>= i look-ahead) (/ 1 i)]
|
||||
|
|
|
|||
|
|
@ -1,74 +1,97 @@
|
|||
require 'set'
|
||||
|
||||
module TicTacToe
|
||||
ROWS = [[1,2,3],[4,5,6],[7,8,9],[1,4,7],[2,5,8],[3,6,9],[1,5,9],[3,5,7]]
|
||||
LINES = [[1,2,3],[4,5,6],[7,8,9],[1,4,7],[2,5,8],[3,6,9],[1,5,9],[3,5,7]]
|
||||
|
||||
class Game
|
||||
def initialize(player1Class, player2Class)
|
||||
@board = Array.new(10)
|
||||
@free_positions = (1..9).to_a
|
||||
@players = [player1Class.new(self), player2Class.new(self)]
|
||||
@turn = rand(2)
|
||||
puts "#{@players[@turn]} goes first."
|
||||
@players[@turn].marker = "X"
|
||||
@players[nextTurn].marker = "O"
|
||||
def initialize(player_1_class, player_2_class)
|
||||
@board = Array.new(10) # we ignore index 0 for convenience
|
||||
|
||||
@current_player_id = 0
|
||||
@players = [player_1_class.new(self, "X"), player_2_class.new(self, "O")]
|
||||
puts "#{current_player} goes first."
|
||||
end
|
||||
attr_reader :free_positions, :board, :turn
|
||||
attr_reader :board, :current_player_id
|
||||
|
||||
def play
|
||||
loop do
|
||||
player = @players[@turn]
|
||||
idx = player.select
|
||||
puts "#{player} selects #{player.marker} position #{idx}"
|
||||
@board[idx] = player.marker
|
||||
@free_positions.delete(idx)
|
||||
place_player_marker(current_player)
|
||||
|
||||
# check for a winner
|
||||
ROWS.each do |row|
|
||||
if row.all? {|idx| @board[idx] == player.marker}
|
||||
puts "#{player} wins!"
|
||||
print_board
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
# no winner, is board full?
|
||||
if @free_positions.empty?
|
||||
if player_has_won?(current_player)
|
||||
puts "#{current_player} wins!"
|
||||
print_board
|
||||
return
|
||||
elsif board_full?
|
||||
puts "It's a draw."
|
||||
print_board
|
||||
return
|
||||
end
|
||||
|
||||
nextTurn!
|
||||
switch_players!
|
||||
end
|
||||
end
|
||||
|
||||
def nextTurn
|
||||
1 - @turn
|
||||
def free_positions
|
||||
Set.new((1..9).select {|position| @board[position].nil?})
|
||||
end
|
||||
|
||||
def nextTurn!
|
||||
@turn = nextTurn
|
||||
def place_player_marker(player)
|
||||
position = player.select_position!
|
||||
puts "#{player} selects #{player.marker} position #{position}"
|
||||
@board[position] = player.marker
|
||||
end
|
||||
|
||||
def player_has_won?(player)
|
||||
LINES.any? do |line|
|
||||
line.all? {|position| @board[position] == player.marker}
|
||||
end
|
||||
end
|
||||
|
||||
def board_full?
|
||||
free_positions.empty?
|
||||
end
|
||||
|
||||
def other_player_id
|
||||
1 - @current_player_id
|
||||
end
|
||||
|
||||
def switch_players!
|
||||
@current_player_id = other_player_id
|
||||
end
|
||||
|
||||
def current_player
|
||||
@players[current_player_id]
|
||||
end
|
||||
|
||||
def opponent
|
||||
@players[nextTurn]
|
||||
@players[other_player_id]
|
||||
end
|
||||
|
||||
def turn_num
|
||||
10 - free_positions.size
|
||||
end
|
||||
|
||||
def print_board
|
||||
display =lambda{|row| row.map {|i| @board[i] ? @board[i] : i}.join("|")}
|
||||
puts display[[1,2,3]], "-+-+-", display[[4,5,6]], "-+-+-", display[[7,8,9]]
|
||||
col_separator, row_separator = " | ", "--+---+--"
|
||||
label_for_position = lambda{|position| @board[position] ? @board[position] : position}
|
||||
|
||||
row_for_display = lambda{|row| row.map(&label_for_position).join(col_separator)}
|
||||
row_positions = [[1,2,3], [4,5,6], [7,8,9]]
|
||||
rows_for_display = row_positions.map(&row_for_display)
|
||||
puts rows_for_display.join("\n" + row_separator + "\n")
|
||||
end
|
||||
end
|
||||
|
||||
class Player
|
||||
def initialize(game)
|
||||
def initialize(game, marker)
|
||||
@game = game
|
||||
@marker = nil
|
||||
@marker = marker
|
||||
end
|
||||
attr_accessor :marker
|
||||
attr_reader :marker
|
||||
end
|
||||
|
||||
class HumanPlayer < Player
|
||||
def select
|
||||
def select_position!
|
||||
@game.print_board
|
||||
loop do
|
||||
print "Select your #{marker} position: "
|
||||
|
|
@ -84,39 +107,75 @@ module TicTacToe
|
|||
end
|
||||
|
||||
class ComputerPlayer < Player
|
||||
def group_row(row)
|
||||
markers = row.group_by {|idx| @game.board[idx]}
|
||||
DEBUG = false # edit this line if necessary
|
||||
|
||||
def group_positions_by_markers(line)
|
||||
markers = line.group_by {|position| @game.board[position]}
|
||||
markers.default = []
|
||||
markers
|
||||
end
|
||||
|
||||
def select
|
||||
def select_position!
|
||||
opponent_marker = @game.opponent.marker
|
||||
|
||||
# look for winning rows
|
||||
for row in ROWS
|
||||
markers = group_row(row)
|
||||
next if markers[nil].length != 1
|
||||
if markers[self.marker].length == 2
|
||||
return markers[nil].first
|
||||
elsif markers[opponent_marker].length == 2
|
||||
idx = markers[nil].first
|
||||
end
|
||||
winning_or_blocking_position = look_for_winning_or_blocking_position(opponent_marker)
|
||||
return winning_or_blocking_position if winning_or_blocking_position
|
||||
|
||||
if corner_trap_defense_needed?
|
||||
return corner_trap_defense_position(opponent_marker)
|
||||
end
|
||||
|
||||
# look for opponent's winning rows to block
|
||||
return idx if idx
|
||||
# could make this smarter by sometimes doing corner trap offense
|
||||
|
||||
# need some logic here to get the computer to pick a smarter position
|
||||
return random_prioritized_position
|
||||
end
|
||||
|
||||
# simply pick a position in order of preference
|
||||
def look_for_winning_or_blocking_position(opponent_marker)
|
||||
for line in LINES
|
||||
markers = group_positions_by_markers(line)
|
||||
next if markers[nil].length != 1
|
||||
if markers[self.marker].length == 2
|
||||
log_debug "winning on line #{line.join}"
|
||||
return markers[nil].first
|
||||
elsif markers[opponent_marker].length == 2
|
||||
log_debug "could block on line #{line.join}"
|
||||
blocking_position = markers[nil].first
|
||||
end
|
||||
end
|
||||
if blocking_position
|
||||
log_debug "blocking at #{blocking_position}"
|
||||
return blocking_position
|
||||
end
|
||||
end
|
||||
|
||||
def corner_trap_defense_needed?
|
||||
corner_positions = [1, 3, 7, 9]
|
||||
opponent_chose_a_corner = corner_positions.any?{|pos| @game.board[pos] != nil}
|
||||
return @game.turn_num == 2 && opponent_chose_a_corner
|
||||
end
|
||||
|
||||
def corner_trap_defense_position(opponent_marker)
|
||||
# if you respond in the center or the opposite corner, the opponent can force you to lose
|
||||
log_debug "defending against corner start by playing adjacent"
|
||||
# playing in an adjacent corner could also be safe, but would require more logic later on
|
||||
opponent_position = @game.board.find_index {|marker| marker == opponent_marker}
|
||||
safe_responses = {1=>[2,4], 3=>[2,6], 7=>[4,8], 9=>[6,8]}
|
||||
return safe_responses[opponent_position].sample
|
||||
end
|
||||
|
||||
def random_prioritized_position
|
||||
log_debug "picking random position, favoring center and then corners"
|
||||
([5] + [1,3,7,9].shuffle + [2,4,6,8].shuffle).find do |pos|
|
||||
@game.free_positions.include?(pos)
|
||||
end
|
||||
end
|
||||
|
||||
def log_debug(message)
|
||||
puts "#{self}: #{message}" if DEBUG
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Computer#{@game.turn}"
|
||||
"Computer#{@game.current_player_id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -125,4 +184,5 @@ include TicTacToe
|
|||
|
||||
Game.new(ComputerPlayer, ComputerPlayer).play
|
||||
puts
|
||||
Game.new(HumanPlayer,ComputerPlayer).play
|
||||
players_with_human = [HumanPlayer, ComputerPlayer].shuffle
|
||||
Game.new(*players_with_human).play
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ for i = 1 to 8
|
|||
b2 = val(mid$(b$,2,1))
|
||||
b3 = val(mid$(b$,3,1))
|
||||
if box$(b1) = "O" and box$(b2) = "O" and box$(b3) = "O" then
|
||||
print "You Loose!"
|
||||
print "You Lose!"
|
||||
goto [playAgain]
|
||||
end if
|
||||
if box$(b1) = "X" and box$(b2) = "X" and box$(b3) = "X" then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue