% main menu
start :-
menu.
menu :-
write ( '1. Test best move procedure' ) , nl , write ( '2. Play against computer' ) , nl , write ( 'Your choice (1-3): ' ) , handle_choice( Choice) .
handle_choice( 1 ) :- ! , test_best_move, menu.
handle_choice( 2 ) :- ! , play_game, menu.
handle_choice
( 3 ) :- ! , write ( 'Exiting program' ) , nl . handle_choice( _) :-
menu.
% testing best move procedure
test_best_move :-
Board = [ x, x, 3 , o, 5 , 6 , 7 , 8 , 9 ] ,
draw_board( Board) ,
best_move( Board, x, Row, Col) ,
% start new game
play_game :-
write ( 'You play X, computer plays O' ) , nl , Board = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] ,
draw_board( Board) ,
game_loop( Board, x) .
% game loop
game_loop( Board, _) :-
check_winner( Board, Winner) , ! ,
game_loop( Board, _) :-
check_draw( Board) , ! ,
game_loop( Board, x) :-
human_move( Board, NewBoard) , ! ,
draw_board( NewBoard) ,
game_loop( NewBoard, o) .
game_loop( Board, o) :-
computer_move( Board, NewBoard) , ! ,
draw_board( NewBoard) ,
game_loop( NewBoard, x) .
% human move
human_move( Board, NewBoard) :-
write ( 'Your move (1-9): ' ) , valid_move( Board, Pos) ,
place_move( Board, Pos, x, NewBoard) , ! .
human_move( Board, NewBoard) :-
write ( 'Invalid move, try again' ) , nl , human_move( Board, NewBoard) .
% computer move
computer_move( Board, NewBoard) :-
best_move( Board, o, Row, Col) ,
Pos
is ( Row
- 1 ) * 3 + Col
, place_move( Board, Pos, o, NewBoard) .
% find best move
best_move( Board, Player, Row, Col) :-
winning_move( Board, Player, Row, Col) , ! .
best_move( Board, Player, Row, Col) :-
opponent( Player, Opp) ,
blocking_move( Board, Opp, Row, Col) , ! .
best_move( Board, _, Row, Col) :-
strategic_move( Board, Row, Col) .
% winning move
winning_move( Board, Player, Row, Col) :-
check_line( Board, Player, Row, Col) .
% blocking move
blocking_move( Board, Opp, Row, Col) :-
check_line( Board, Opp, Row, Col) .
% check line
check_line( Board, P, Row, Col) :-
line( R1, C1, R2, C2, R3, C3) ,
pos_value( Board, R1, C1, V1) ,
pos_value( Board, R2, C2, V2) ,
pos_value( Board, R3, C3, V3) ,
two_in_line( V1, V2, V3, P, Row, Col, R1, C1, R2, C2, R3, C3) .
two_in_line
( P
, P
, V3
, P
, R3
, C3
, _
, _
, _
, _
, R3
, C3
) :- number ( V3
) , ! . two_in_line
( P
, V2
, P
, P
, R2
, C2
, _
, _
, R2
, C2
, _
, _
) :- number ( V2
) , ! . two_in_line
( V1
, P
, P
, P
, R1
, C1
, R1
, C1
, _
, _
, _
, _
) :- number ( V1
) , ! .
% strategic move
strategic_move( Board, Row, Col) :-
priority( Row, Col) ,
Pos
is ( Row
- 1 ) * 3 + Col
, valid_move( Board, Pos) , ! .
% position priority
priority( 2 , 2 ) .
priority( 1 , 1 ) .
priority( 1 , 3 ) .
priority( 3 , 1 ) .
priority( 3 , 3 ) .
priority( 1 , 2 ) .
priority( 2 , 1 ) .
priority( 2 , 3 ) .
priority( 3 , 2 ) .
% lines to check
line( 1 , 1 , 1 , 2 , 1 , 3 ) .
line( 2 , 1 , 2 , 2 , 2 , 3 ) .
line( 3 , 1 , 3 , 2 , 3 , 3 ) .
line( 1 , 1 , 2 , 1 , 3 , 1 ) .
line( 1 , 2 , 2 , 2 , 3 , 2 ) .
line( 1 , 3 , 2 , 3 , 3 , 3 ) .
line( 1 , 1 , 2 , 2 , 3 , 3 ) .
line( 1 , 3 , 2 , 2 , 3 , 1 ) .
% opponent
opponent( x, o) .
opponent( o, x) .
% check winner
check_winner( Board, Winner) :-
line( R1, C1, R2, C2, R3, C3) ,
pos_value( Board, R1, C1, Winner) ,
pos_value( Board, R2, C2, Winner) ,
pos_value( Board, R3, C3, Winner) , ! .
% check draw
check_draw( Board) :-
\+ ( member
( X
, Board
) , number ( X
) ) .
% valid move check
valid_move( Board, Pos) :-
Pos >= 1 ,
Pos =< 9 ,
nth1( Pos, Board, Val) ,
% place move
place_move( [ _| T] , 1 , Player, [ Player| T] ) :- ! .
place_move( [ H| T] , Pos, Player, [ H| R] ) :-
Pos > 1 ,
place_move( T, Pos1, Player, R) .
% get position value
pos_value( Board, Row, Col, Val) :-
Pos
is ( Row
- 1 ) * 3 + Col
, nth1( Pos, Board, Val) .
% draw board
draw_board( Board) :-
nth1( 1 , Board, V1) , nth1( 2 , Board, V2) , nth1( 3 , Board, V3) ,
nth1( 4 , Board, V4) , nth1( 5 , Board, V5) , nth1( 6 , Board, V6) ,
nth1( 7 , Board, V7) , nth1( 8 , Board, V8) , nth1( 9 , Board, V9) ,
format( ' ~w | ~w | ~w ~n' , [ V1, V2, V3] ) ,
format( ' ~w | ~w | ~w ~n' , [ V4, V5, V6] ) ,
format( ' ~w | ~w | ~w ~n' , [ V7, V8, V9] ) ,
start.
JSBtYWluIG1lbnUKc3RhcnQgOi0gCiAgICB3cml0ZSgnVGljLVRhYy1Ub2UnKSwgbmwsCiAgICB3cml0ZSgnTGFib3JhdG9yeSB3b3JrIDcnKSwgbmwsIG5sLAogICAgbWVudS4KCm1lbnUgOi0gCiAgICB3cml0ZSgnU2VsZWN0IGFjdGlvbjonKSwgbmwsCiAgICB3cml0ZSgnMS4gVGVzdCBiZXN0IG1vdmUgcHJvY2VkdXJlJyksIG5sLAogICAgd3JpdGUoJzIuIFBsYXkgYWdhaW5zdCBjb21wdXRlcicpLCBubCwKICAgIHdyaXRlKCczLiBFeGl0JyksIG5sLAogICAgd3JpdGUoJ1lvdXIgY2hvaWNlICgxLTMpOiAnKSwKICAgIHJlYWQoQ2hvaWNlKSwKICAgIGhhbmRsZV9jaG9pY2UoQ2hvaWNlKS4KCmhhbmRsZV9jaG9pY2UoMSkgOi0gISwgdGVzdF9iZXN0X21vdmUsIG1lbnUuCmhhbmRsZV9jaG9pY2UoMikgOi0gISwgcGxheV9nYW1lLCBtZW51LgpoYW5kbGVfY2hvaWNlKDMpIDotICEsIHdyaXRlKCdFeGl0aW5nIHByb2dyYW0nKSwgbmwuCmhhbmRsZV9jaG9pY2UoXykgOi0gCiAgICB3cml0ZSgnSW52YWxpZCBjaG9pY2UnKSwgbmwsIG5sLCAKICAgIG1lbnUuCgolIHRlc3RpbmcgYmVzdCBtb3ZlIHByb2NlZHVyZQp0ZXN0X2Jlc3RfbW92ZSA6LSAKICAgIG5sLCB3cml0ZSgnQmVzdCBtb3ZlIHNlYXJjaCB0ZXN0JyksIG5sLAogICAgQm9hcmQgPSBbeCwgeCwgMywgbywgNSwgNiwgNywgOCwgOV0sCiAgICB3cml0ZSgnVGVzdCBib2FyZDonKSwgbmwsCiAgICBkcmF3X2JvYXJkKEJvYXJkKSwKICAgIGJlc3RfbW92ZShCb2FyZCwgeCwgUm93LCBDb2wpLAogICAgd3JpdGUoJ0Jlc3QgbW92ZSBmb3IgWDogcm93ICcpLCB3cml0ZShSb3cpLAogICAgd3JpdGUoJywgY29sdW1uICcpLCB3cml0ZShDb2wpLCBubCwgbmwuCgolIHN0YXJ0IG5ldyBnYW1lCnBsYXlfZ2FtZSA6LSAKICAgIG5sLCB3cml0ZSgnTmV3IGdhbWUnKSwgbmwsCiAgICB3cml0ZSgnWW91IHBsYXkgWCwgY29tcHV0ZXIgcGxheXMgTycpLCBubCwKICAgIHdyaXRlKCdFbnRlciBjZWxsIG51bWJlciAxLTknKSwgbmwsIG5sLAogICAgQm9hcmQgPSBbMSwyLDMsNCw1LDYsNyw4LDldLAogICAgZHJhd19ib2FyZChCb2FyZCksCiAgICBnYW1lX2xvb3AoQm9hcmQsIHgpLgoKJSBnYW1lIGxvb3AKZ2FtZV9sb29wKEJvYXJkLCBfKSA6LSAKICAgIGNoZWNrX3dpbm5lcihCb2FyZCwgV2lubmVyKSwgISwKICAgIHdyaXRlKCdXaW5uZXI6ICcpLCB3cml0ZShXaW5uZXIpLCBubCwgbmwuCgpnYW1lX2xvb3AoQm9hcmQsIF8pIDotIAogICAgY2hlY2tfZHJhdyhCb2FyZCksICEsCiAgICB3cml0ZSgnRHJhdyEnKSwgbmwsIG5sLgoKZ2FtZV9sb29wKEJvYXJkLCB4KSA6LSAKICAgIGh1bWFuX21vdmUoQm9hcmQsIE5ld0JvYXJkKSwgISwKICAgIGRyYXdfYm9hcmQoTmV3Qm9hcmQpLAogICAgZ2FtZV9sb29wKE5ld0JvYXJkLCBvKS4KCmdhbWVfbG9vcChCb2FyZCwgbykgOi0gCiAgICBjb21wdXRlcl9tb3ZlKEJvYXJkLCBOZXdCb2FyZCksICEsCiAgICB3cml0ZSgnQ29tcHV0ZXIgbW92ZTonKSwgbmwsCiAgICBkcmF3X2JvYXJkKE5ld0JvYXJkKSwKICAgIGdhbWVfbG9vcChOZXdCb2FyZCwgeCkuCgolIGh1bWFuIG1vdmUKaHVtYW5fbW92ZShCb2FyZCwgTmV3Qm9hcmQpIDotIAogICAgd3JpdGUoJ1lvdXIgbW92ZSAoMS05KTogJyksCiAgICByZWFkKFBvcyksCiAgICB2YWxpZF9tb3ZlKEJvYXJkLCBQb3MpLAogICAgcGxhY2VfbW92ZShCb2FyZCwgUG9zLCB4LCBOZXdCb2FyZCksICEuCgpodW1hbl9tb3ZlKEJvYXJkLCBOZXdCb2FyZCkgOi0KICAgIHdyaXRlKCdJbnZhbGlkIG1vdmUsIHRyeSBhZ2FpbicpLCBubCwKICAgIGh1bWFuX21vdmUoQm9hcmQsIE5ld0JvYXJkKS4KCiUgY29tcHV0ZXIgbW92ZQpjb21wdXRlcl9tb3ZlKEJvYXJkLCBOZXdCb2FyZCkgOi0gCiAgICBiZXN0X21vdmUoQm9hcmQsIG8sIFJvdywgQ29sKSwKICAgIFBvcyBpcyAoUm93IC0gMSkgKiAzICsgQ29sLAogICAgcGxhY2VfbW92ZShCb2FyZCwgUG9zLCBvLCBOZXdCb2FyZCkuCgolIGZpbmQgYmVzdCBtb3ZlCmJlc3RfbW92ZShCb2FyZCwgUGxheWVyLCBSb3csIENvbCkgOi0gCiAgICB3aW5uaW5nX21vdmUoQm9hcmQsIFBsYXllciwgUm93LCBDb2wpLCAhLgoKYmVzdF9tb3ZlKEJvYXJkLCBQbGF5ZXIsIFJvdywgQ29sKSA6LSAKICAgIG9wcG9uZW50KFBsYXllciwgT3BwKSwKICAgIGJsb2NraW5nX21vdmUoQm9hcmQsIE9wcCwgUm93LCBDb2wpLCAhLgoKYmVzdF9tb3ZlKEJvYXJkLCBfLCBSb3csIENvbCkgOi0gCiAgICBzdHJhdGVnaWNfbW92ZShCb2FyZCwgUm93LCBDb2wpLgoKJSB3aW5uaW5nIG1vdmUKd2lubmluZ19tb3ZlKEJvYXJkLCBQbGF5ZXIsIFJvdywgQ29sKSA6LSAKICAgIGNoZWNrX2xpbmUoQm9hcmQsIFBsYXllciwgUm93LCBDb2wpLgoKJSBibG9ja2luZyBtb3ZlCmJsb2NraW5nX21vdmUoQm9hcmQsIE9wcCwgUm93LCBDb2wpIDotIAogICAgY2hlY2tfbGluZShCb2FyZCwgT3BwLCBSb3csIENvbCkuCgolIGNoZWNrIGxpbmUKY2hlY2tfbGluZShCb2FyZCwgUCwgUm93LCBDb2wpIDotIAogICAgbGluZShSMSwgQzEsIFIyLCBDMiwgUjMsIEMzKSwKICAgIHBvc192YWx1ZShCb2FyZCwgUjEsIEMxLCBWMSksCiAgICBwb3NfdmFsdWUoQm9hcmQsIFIyLCBDMiwgVjIpLAogICAgcG9zX3ZhbHVlKEJvYXJkLCBSMywgQzMsIFYzKSwKICAgIHR3b19pbl9saW5lKFYxLCBWMiwgVjMsIFAsIFJvdywgQ29sLCBSMSwgQzEsIFIyLCBDMiwgUjMsIEMzKS4KCnR3b19pbl9saW5lKFAsIFAsIFYzLCBQLCBSMywgQzMsIF8sIF8sIF8sIF8sIFIzLCBDMykgOi0gbnVtYmVyKFYzKSwgIS4KdHdvX2luX2xpbmUoUCwgVjIsIFAsIFAsIFIyLCBDMiwgXywgXywgUjIsIEMyLCBfLCBfKSA6LSBudW1iZXIoVjIpLCAhLgp0d29faW5fbGluZShWMSwgUCwgUCwgUCwgUjEsIEMxLCBSMSwgQzEsIF8sIF8sIF8sIF8pIDotIG51bWJlcihWMSksICEuCgolIHN0cmF0ZWdpYyBtb3ZlCnN0cmF0ZWdpY19tb3ZlKEJvYXJkLCBSb3csIENvbCkgOi0gCiAgICBwcmlvcml0eShSb3csIENvbCksCiAgICBQb3MgaXMgKFJvdyAtIDEpICogMyArIENvbCwKICAgIHZhbGlkX21vdmUoQm9hcmQsIFBvcyksICEuCgolIHBvc2l0aW9uIHByaW9yaXR5CnByaW9yaXR5KDIsIDIpLgpwcmlvcml0eSgxLCAxKS4KcHJpb3JpdHkoMSwgMykuCnByaW9yaXR5KDMsIDEpLgpwcmlvcml0eSgzLCAzKS4KcHJpb3JpdHkoMSwgMikuCnByaW9yaXR5KDIsIDEpLgpwcmlvcml0eSgyLCAzKS4KcHJpb3JpdHkoMywgMikuCgolIGxpbmVzIHRvIGNoZWNrCmxpbmUoMSwgMSwgMSwgMiwgMSwgMykuCmxpbmUoMiwgMSwgMiwgMiwgMiwgMykuCmxpbmUoMywgMSwgMywgMiwgMywgMykuCmxpbmUoMSwgMSwgMiwgMSwgMywgMSkuCmxpbmUoMSwgMiwgMiwgMiwgMywgMikuCmxpbmUoMSwgMywgMiwgMywgMywgMykuCmxpbmUoMSwgMSwgMiwgMiwgMywgMykuCmxpbmUoMSwgMywgMiwgMiwgMywgMSkuCgolIG9wcG9uZW50Cm9wcG9uZW50KHgsIG8pLgpvcHBvbmVudChvLCB4KS4KCiUgY2hlY2sgd2lubmVyCmNoZWNrX3dpbm5lcihCb2FyZCwgV2lubmVyKSA6LSAKICAgIGxpbmUoUjEsIEMxLCBSMiwgQzIsIFIzLCBDMyksCiAgICBwb3NfdmFsdWUoQm9hcmQsIFIxLCBDMSwgV2lubmVyKSwKICAgIFwrIG51bWJlcihXaW5uZXIpLAogICAgcG9zX3ZhbHVlKEJvYXJkLCBSMiwgQzIsIFdpbm5lciksCiAgICBwb3NfdmFsdWUoQm9hcmQsIFIzLCBDMywgV2lubmVyKSwgIS4KCiUgY2hlY2sgZHJhdwpjaGVja19kcmF3KEJvYXJkKSA6LSAKICAgIFwrIChtZW1iZXIoWCwgQm9hcmQpLCBudW1iZXIoWCkpLgoKJSB2YWxpZCBtb3ZlIGNoZWNrCnZhbGlkX21vdmUoQm9hcmQsIFBvcykgOi0gCiAgICBpbnRlZ2VyKFBvcyksCiAgICBQb3MgPj0gMSwgCiAgICBQb3MgPTwgOSwKICAgIG50aDEoUG9zLCBCb2FyZCwgVmFsKSwKICAgIG51bWJlcihWYWwpLgoKJSBwbGFjZSBtb3ZlCnBsYWNlX21vdmUoW198VF0sIDEsIFBsYXllciwgW1BsYXllcnxUXSkgOi0gIS4KcGxhY2VfbW92ZShbSHxUXSwgUG9zLCBQbGF5ZXIsIFtIfFJdKSA6LSAKICAgIFBvcyA+IDEsCiAgICBQb3MxIGlzIFBvcyAtIDEsCiAgICBwbGFjZV9tb3ZlKFQsIFBvczEsIFBsYXllciwgUikuCgolIGdldCBwb3NpdGlvbiB2YWx1ZQpwb3NfdmFsdWUoQm9hcmQsIFJvdywgQ29sLCBWYWwpIDotIAogICAgUG9zIGlzIChSb3cgLSAxKSAqIDMgKyBDb2wsCiAgICBudGgxKFBvcywgQm9hcmQsIFZhbCkuCgolIGRyYXcgYm9hcmQKZHJhd19ib2FyZChCb2FyZCkgOi0gCiAgICBudGgxKDEsIEJvYXJkLCBWMSksIG50aDEoMiwgQm9hcmQsIFYyKSwgbnRoMSgzLCBCb2FyZCwgVjMpLAogICAgbnRoMSg0LCBCb2FyZCwgVjQpLCBudGgxKDUsIEJvYXJkLCBWNSksIG50aDEoNiwgQm9hcmQsIFY2KSwKICAgIG50aDEoNywgQm9hcmQsIFY3KSwgbnRoMSg4LCBCb2FyZCwgVjgpLCBudGgxKDksIEJvYXJkLCBWOSksCiAgICBmb3JtYXQoJyB+dyB8IH53IHwgfncgfm4nLCBbVjEsIFYyLCBWM10pLAogICAgd3JpdGUoJy0tLS0tLS0tLS0tJyksIG5sLAogICAgZm9ybWF0KCcgfncgfCB+dyB8IH53IH5uJywgW1Y0LCBWNSwgVjZdKSwKICAgIHdyaXRlKCctLS0tLS0tLS0tLScpLCBubCwKICAgIGZvcm1hdCgnIH53IHwgfncgfCB+dyB+bicsIFtWNywgVjgsIFY5XSksCiAgICBubC4KICAgIAogICAgc3RhcnQuIAo=