Created
June 23, 2015 17:04
-
-
Save andy-williams/f0fc7357d4433276c47a to your computer and use it in GitHub Desktop.
Some old code I've written during my time in University. This is a tic-tact-toe game written entirely in prolog. Includes board generator and support for 2 players.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/************************************************* | |
* CHA 2555 - ARTIFICIAL INTELLIGENCE (2012-2013) | |
* Andrew Williams | |
* u0857629@unimail.hud.ac.uk | |
* BSc (Hons) Software Development | |
************************************************/ | |
/******** | |
* XOXO | |
******** | |
XOXO is a game that is a modified version of the classic tic-tac-toe game. | |
AI player uses a minimax implementation for making decisions. | |
******** | |
* Rules | |
******** | |
as per the classic tic-tac-toe game, the symbols for each player are 'x' and 'o' where 'x' always starts first. | |
In order to win, a player needs to have four of their own symbol in a line | |
for example: | |
| x | x | x | x | | |
or | |
|x| | |
|x| | |
|x| | |
|x| | |
or | |
|x||0||0||0| | |
|0||x||0||0| | |
|0||0||x||0| | |
|0||0||0||x| | |
************* | |
* Variables | |
************** | |
B - Board | |
P - Player (human or computer) | |
PS - Player Symbol (x or o) | |
D - Depth of minimax search tree | |
*************** | |
* Facts | |
**************/ | |
% the number of the next player | |
next_player(x, o). | |
next_player(o, x). | |
% human and computer players declaration | |
player(x, human). | |
player(o, computer). | |
% empty symbol | |
empty_symbol(e). | |
/********* | |
* GAME | |
*********/ | |
default_game :- | |
make_board(4,4,B), !, | |
game(B). | |
game(B) :- | |
game(B, x). | |
game(B, PS) :- | |
pretty_print(B), | |
next_player(PS, PS1), | |
write('Checking if '), | |
write(PS1), write(' has won...'), nl, | |
fail. | |
% Win through horizontal matching | |
game(B, PS) :- | |
next_player(PS, PS1), | |
horizontal(PS1, B), | |
write('Player '), write(PS1), | |
write(' has won the game.'), nl. | |
% Win through vertical matching | |
game(B, PS) :- | |
next_player(PS, PS1), | |
vertical(PS1, B), | |
write('Player '), write(PS1), | |
write(' has won the game.'), nl. | |
% Win through diagonal matching | |
game(B, PS) :- | |
next_player(PS, PS1), | |
diagonal(PS1, B), | |
write('Player '), write(PS1), | |
write(' has won the game.'), nl. | |
game(B, PS) :- | |
player(PS, P), | |
make_move(PS, P, B, B_out), | |
next_player(PS, NPS), | |
game(B_out, NPS), !. | |
% Human's turn | |
make_move(PS, human, B_in, B_out) :- | |
write('Player '), write(PS), write(':'), nl, | |
write('Type in where you want to place a disc mark(X,Y).'), | |
nl, | |
read(mark(X,Y)),nl, | |
mark(x(X, Y, PS), B_in, B_out). | |
% Computer's turn | |
make_move(PS, computer, B_in, B_out) :- | |
write('Computer is thinking for a move...'),nl, | |
write('Type in where you want to place a disc mark(X,Y).'), | |
nl, | |
read(mark(X,Y)),nl, | |
mark(x(X, Y, PS), B_in, B_out). | |
% Mark a cell in the board | |
mark(x(X, Y, PS), B_in, B_out) :- | |
valid_mark_placement(X,Y,B_in), | |
empty_symbol(E), | |
remove(x(X,Y,E), B_in, B_new), | |
append([x(X,Y,PS)], B_new, B_out). | |
% Valid if it's an empty symbol | |
valid_mark_placement(X,Y,B) :- | |
empty_symbol(E), | |
member(x(X,Y,E), B). | |
/********** | |
* TESTS | |
*********/ | |
game_win_vertical :- | |
make_board(6,6,B),!, | |
mark(x(1,1,x), B, B1), | |
mark(x(2,1,x), B1, B2), | |
mark(x(3,1,x), B2, B3), | |
mark(x(4,1,x), B3, B4), | |
game(B4, o). | |
game_win_vertical_2 :- | |
make_board(6,6,B),!, | |
mark(x(1,2,x), B, B1), | |
mark(x(2,2,x), B1, B2), | |
mark(x(3,2,x), B2, B3), | |
mark(x(4,2,x), B3, B4), | |
game(B4, o). | |
game_win_horizontal :- | |
make_board(4,4,B),!, | |
mark(x(1,1,x), B, B1), | |
mark(x(1,2,x), B1, B2), | |
mark(x(1,3,x), B2, B3), | |
mark(x(1,4,x), B3, B4), | |
game(B4, o). | |
game_win_horizontal_2 :- | |
make_board(4,4,B),!, | |
mark(x(2,1,x), B, B1), | |
mark(x(2,2,x), B1, B2), | |
mark(x(2,3,x), B2, B3), | |
mark(x(2,4,x), B3, B4), | |
game(B4, o). | |
game_win_diagonal :- | |
make_board(6,6,B),!, | |
mark(x(1,1,x), B, B1), | |
mark(x(2,2,x), B1, B2), | |
mark(x(3,3,x), B2, B3), | |
mark(x(4,4,x), B3, B4), | |
game(B4, o). | |
game_win_diagonal_2 :- | |
make_board(6,6,B),!, | |
mark(x(1,4,x), B, B1), | |
mark(x(2,3,x), B1, B2), | |
mark(x(3,2,x), B2, B3), | |
mark(x(4,1,x), B3, B4), | |
pretty_print(B4), | |
game(B4, o). | |
/********* | |
END GAME | |
*********/ | |
/*********************** | |
* LINE MATCHER: | |
*********************** | |
Used for finding Symbols | |
that create Horizontal, | |
Vertical and Diagonal | |
lines | |
*/ | |
% Check if Player Symbol makes a horizontal line | |
horizontal(PS, B) :- | |
write('Checking horizontally...'), nl, | |
member(x(X, Y, PS), B), | |
Y1 is Y+1, Y2 is Y+2, Y3 is Y+3, | |
member(x(X, Y1, PS), B), | |
member(x(X, Y2, PS), B), | |
member(x(X, Y3, PS), B). | |
% Check if Player Symbol makes a vertical line | |
vertical(PS, B) :- | |
write('Checking vertically...'), nl, | |
member(x(X, Y, PS), B), | |
X1 is X+1, X2 is X+2, X3 is X+3, | |
member(x(X1, Y, PS), B), | |
member(x(X2, Y, PS), B), | |
member(x(X3, Y, PS), B). | |
% Check if Player Symbol makes a diagonal line | |
diagonal(PS, B) :- | |
write('Checking diagonally...'), nl, | |
member(x(X, Y, PS), B), | |
X1 is X+1, X2 is X+2, X3 is X+3, | |
Y1 is Y+1, Y2 is Y+2, Y3 is Y+3, | |
member(x(X1, Y1, PS), B), | |
member(x(X2, Y2, PS), B), | |
member(x(X3, Y3, PS), B). | |
diagonal(PS, B) :- | |
write('Checking diagonally...'), nl, | |
member(x(X, Y, PS), B), | |
X1 is X+1, X2 is X+2, X3 is X+3, | |
Y1 is Y-1, Y2 is Y-2, Y3 is Y-3, | |
member(x(X1, Y1, PS), B), | |
member(x(X2, Y2, PS), B), | |
member(x(X3, Y3, PS), B). | |
/********* | |
* TESTS | |
********/ | |
test_vertical :- | |
make_board(6,6,B),!, | |
mark(x(1,1,x), B, B1), | |
mark(x(2,1,x), B1, B2), | |
mark(x(3,1,x), B2, B3), | |
mark(x(4,1,x), B3, B4), | |
vertical(x, B4). | |
test_vertical_2 :- | |
make_board(6,6,B),!, | |
mark(x(1,2,x), B, B1), | |
mark(x(2,2,x), B1, B2), | |
mark(x(3,2,x), B2, B3), | |
mark(x(4,2,x), B3, B4), | |
vertical(x, B4). | |
test_horizontal :- | |
make_board(4,4,B),!, | |
mark(x(1,1,x), B, B1), | |
mark(x(1,2,x), B1, B2), | |
mark(x(1,3,x), B2, B3), | |
mark(x(1,4,x), B3, B4), | |
horizontal(x, B4). | |
test_horizontal_2 :- | |
make_board(4,4,B),!, | |
mark(x(2,1,x), B, B1), | |
mark(x(2,2,x), B1, B2), | |
mark(x(2,3,x), B2, B3), | |
mark(x(2,4,x), B3, B4), | |
horizontal(x, B4). | |
test_diagonal :- | |
make_board(6,6,B),!, | |
mark(x(1,1,x), B, B1), | |
mark(x(2,2,x), B1, B2), | |
mark(x(3,3,x), B2, B3), | |
mark(x(4,4,x), B3, B4), | |
pretty_print(B4), | |
diagonal(x, B4). | |
test_diagonal_2 :- | |
make_board(6,6,B),!, | |
mark(x(1,4,x), B, B1), | |
mark(x(2,3,x), B1, B2), | |
mark(x(3,2,x), B2, B3), | |
mark(x(4,1,x), B3, B4), | |
pretty_print(B4), | |
diagonal(x, B4). | |
/******************* | |
* END LINE MATCHER | |
*******************/ | |
/******************* | |
* BOARD GENERATOR | |
******************** | |
Used for generating boards automatically. | |
************* | |
* Variables | |
************** | |
W - Width | |
H - Height | |
I - used for controlling row recursions | |
J - used for controlling col recursions | |
B_out - board output | |
B_in - board input | |
*/ | |
%Generate a board | |
make_board(W, H, B_out) :- | |
nl, | |
write('Generating board...'), | |
make_board(W, H, 1, [], B_out). | |
% End if J == H + 1. | |
make_board(W, H, I, B_in, B_out) :- | |
write(I), write(' is the value of I'), nl, | |
H1 is H+1, | |
I == H1, | |
% Assign the last board modication as our board output | |
B_out = B_in. | |
% Recursive - make_board until J == H + 1 | |
make_board(W, H, I, B_in, B_out) :- | |
add_row(W, I, 1, B_in, B_new), | |
I1 is I+1, | |
make_board(W, H, I1, B_new, B_out). | |
% End if J == W+1 | |
add_row(W, I, J, B_in, B_out) :- | |
write(J), write(' is the value of J'), nl, | |
W1 is W+1, | |
J == W1, | |
% Assign the last board modication as our board output | |
B_out = B_in. | |
% Recursive - add_row until J == W+1. | |
add_row(W, I, J, B_in, B_out) :- | |
empty_symbol(E), | |
append([x(I,J,E)], B_in, B_new), | |
J1 is J+1, | |
add_row(W, I, J1, B_new, B_out). | |
/********* | |
* TESTS | |
********/ | |
test_board_gen :- | |
test_board_gen(4, 4). | |
test_board_gen_2 :- | |
test_board_gen(6, 6). | |
test_board_gen(W, H) :- | |
make_board(W, H, B), pretty_print(B). | |
/********************** | |
* END BOARD GENERATOR | |
**********************/ | |
% GENERAL UTILITIES | |
% remove(a,l,m) - a is a member of l, m is l without a | |
remove(X, [X|Y], Y) :-!. | |
remove(X, [X1|Y], [X1|Z]) :- remove(X,Y,Z),!. | |
member(X,[X|_]). | |
member(X,[_|L]) :- member(X,L). | |
pretty_print(B):- | |
nl, | |
print_each_row(1, B), | |
nl. | |
% finished if no elements in row "I" in board: | |
print_each_row(I,B) :- | |
\+ member(x(I,_,_),B),!. | |
print_each_row(I,B) :- | |
print_out_row(I,1,B), | |
J is I+1, | |
print_each_row(J,B),!. | |
print_out_row(I,J,B) :- | |
member(x(I,J,Piece),B), | |
write(Piece),write(' '), | |
J1 is J+1, | |
print_out_row(I,J1,B). | |
% to get here means we have got to the end .. | |
print_out_row(_,_,_) :- | |
nl. | |
testp :- get_board(medium,B), pretty_print(B). | |
/******* | |
* END | |
*******/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment