% noughts and crosses game :- discontiguous( rule/3 ). :- dynamic( sumvec/1 ). :- dynamic( current/1 ). :- dynamic( x_won/0 ). :- dynamic( o_won/0 ). blank( [b, b, b, b, b, b, b, b, b, b, b, b, b, b, b, b] ). % horizontal row( [o, o, o, o, b, b, b, b, b, b, b, b, b, b, b, b] ). row( [b, b, b, b, o, o, o, o, b, b, b, b, b, b, b, b] ). row( [b, b, b, b, b, b, b, b, o, o, o, o, b, b, b, b] ). row( [b, b, b, b, b, b, b, b, b, b, b, b, o, o, o, o] ). % vertical row( [o, b, b, b, o, b, b, b, o, b, b, b, o, b, b, b] ). row( [b, o, b, b, b, o, b, b, b, o, b, b, b, o, b, b] ). row( [b, b, o, b, b, b, o, b, b, b, o, b, b, b, o, b] ). row( [b, b, b, o, b, b, b, o, b, b, b, o, b, b, b, o] ). % diagonal row( [o, b, b, b, b, o, b, b, b, b, o, b, b, b, b, o] ). row( [b, b, b, o, b, b, o, b, b, o, b, b, o, b, b, b] ). % e.g. current( [o, o, b, x, x, b, b, b, b] ). % pickoutrow( [], [], [] ) :- !. pickoutrow( [o|Rt], [Ch|Ct], [Ch|A] ) :- !, pickoutrow( Rt, Ct, A ). pickoutrow( [b|Rt], [Ch|Ct], A ) :- !, pickoutrow( Rt, Ct, A ). count_symb_inner( [], A, A, S ). count_symb_inner( [S|T], A, R, S ) :- !, A1 is A + 1, count_symb_inner( T, A1, R, S ). count_symb_inner( [_|T], A, R, S ) :- count_symb_inner( T, A, R, S ). count_symb( L, R, S ) :- count_symb_inner( L, 0, R, S ). add_in_blank( Sym, [], [], [] ). add_in_blank( Sym, [o|Rt], [b|B], [Sym|B2] ) :- !, add_in_blank( Sym, Rt, B, B2 ). add_in_blank( Sym, [_|Rt], [H|B], [H|B2] ) :- add_in_blank( Sym, Rt, B , B2 ). % GETTING THE WINNING LINE (always gives o tho') % ---------------------------------------------- winning_line( Board, Line ) :- row( R ), pickoutrow( R, Board, PR ), ( count_symb( PR, 4, o ) ; count_symb( PR, 4, x ) ), !, Line = R. % RULE 1 (computer is o). % ------ opposer( o, x ). opposer( x, o ). rule( Player, Board, NewBoard ) :- row( R ), pickoutrow( R, Board, PR ), count_symb( PR, 3, Player ), count_symb( PR, 1, b ), !, add_in_blank( Player, R, Board, NewBoard ), ( Player = x -> asserta( x_won ) ; asserta( o_won ) ). % RULE 2 % ------ rule( Player, Board, NewBoard ) :- row( R ), pickoutrow( R, Board, PR ), opposer( Player, Opposer ), count_symb( PR, 3, Opposer ), count_symb( PR, 1, b ), !, add_in_blank( Player, R, Board, NewBoard ). % RULE 3 % ------ add_lists( [], [], [] ). add_lists( [Ah|At], [Bh|Bt], [Sh|St] ) :- Sh is Ah + Bh, add_lists( At, Bt, St ). blank_at_pos_inner( [], P ) :- fail. blank_at_pos_inner( [b|Rt], P, P ) :- !. blank_at_pos_inner( [_|Rt], P, P ) :- fail. blank_at_pos_inner( [_|Rt], P, C ) :- C1 is C + 1, blank_at_pos_inner( Rt, P, C1 ). blank_at_pos( R, P ) :- blank_at_pos_inner( R, P, 1 ). row_contains_inner( [Rh|Rt], P, P ) :- !, Rh = o. row_contains_inner( [Rh|Rt], P, Q ) :- Q1 is Q + 1, row_contains_inner( Rt, P, Q1 ). row_contains( R, P ) :- row_contains_inner( R, P, 1 ). construct_scores_inner( Player, R, B, [], 17 ) :- !. construct_scores_inner( Player, R, B, [1|St], P ) :- blank_at_pos( B, P ), row_contains( R, P ), pickoutrow( R, B, PR ), count_symb( PR, 4, b ), !, P1 is P + 1, construct_scores_inner( Player, R, B, St, P1 ). construct_scores_inner( Player, R, B, [2|St], P ) :- blank_at_pos( B, P ), row_contains( R, P ), pickoutrow( R, B, PR ), count_symb( PR, 3, b ), count_symb( PR, 1, Player ), !, P1 is P + 1, construct_scores_inner( Player, R, B, St, P1 ). construct_scores_inner( Player, R, B, [8|St], P ) :- blank_at_pos( B, P ), row_contains( R, P ), pickoutrow( R, B, PR ), count_symb( PR, 2, b ), count_symb( PR, 2, Player ), !, P1 is P + 1, construct_scores_inner( Player, R, B, St, P1 ). construct_scores_inner( Player, R, B, [0|St], P ) :- P1 is P + 1, construct_scores_inner( Player, R, B, St, P1 ). construct_scores_2( P, B ) :- row( R ), construct_scores_inner( P, R, B, S, 1 ), sumvec( SUM ), add_lists( SUM, S, New ), abolish( sumvec/1 ), asserta( sumvec( New ) ), fail. construct_scores_2( P, B ) :- true. construct_scores( P, B, S ) :- abolish( sumvec/1 ), asserta( sumvec( [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ) ), !, construct_scores_2( P, B ), sumvec( S ). max_list_inner( [], M, M ). max_list_inner( [H|T], N, M ) :- H > N, !, max_list_inner( T, H, M ). max_list_inner( [H|T], N, M ) :- max_list_inner( T, N, M ). max_list( [H|T], M ) :- max_list_inner( [H|T], H, M ). play_in_first_max( P, [], [], M, [] ) :- !, M = -1. play_in_first_max( P, [b|Bt], [M|St], M, [P|NBt] ) :- !, play_in_first_max( P, Bt, St, -1, NBt ). play_in_first_max( P, [Bh|Bt], [_|St], M, [Bh|NBt] ) :- play_in_first_max( P, Bt, St, M, NBt ). rule( Player, Board, NewBoard ) :- construct_scores( Player, Board, Scores ), max_list( Scores, Max ), play_in_first_max( Player, Board, Scores, Max, NewBoard ). play_in_given_place_inner( P, [], [], Pos, A ) :- !, Pos = -1. play_in_given_place_inner( P, [b|Bt], [P|St], Pos, Pos ) :- !, A1 is Pos + 1, play_in_given_place_inner( P, Bt, St, -1, A1 ). play_in_given_place_inner( P, [Bh|Bt], [Bh|St], Pos, A ) :- A1 is A + 1, play_in_given_place_inner( P, Bt, St, Pos, A1 ). play_in_given_place( Player, Board, NewBoard, Position ) :- play_in_given_place_inner( Player, Board, NewBoard, Position, 1 ). play_me_inner :- current( B ), rule( o, B, B2 ), write( B2 ), nl, write( 'Play where? ' ), read( Pos ), play_in_given_place( x, B2, B3, Pos ), write( B3 ), nl, abolish( current/1 ), asserta( current( B3 ) ), play_me_inner. play_me :- abolish( current/1 ), blank( B ), asserta( current( B ) ), play_me_inner. play_self_inner :- current( B ), rule( o, B, B2 ), write( B2 ), nl, rule( x, B2, B3 ), write( B3 ), nl, abolish( current/1 ), asserta( current( B3 ) ), play_self_inner. play_self :- abolish( current/1 ), blank( B ), asserta( current( B ) ), play_self_inner. % state remembering interface % --------------------------- reset_board( B ) :- abolish( current/1 ), blank( B ), asserta( current( B ) ), abolish( x_won/0 ), abolish( o_won/0 ). human_x_move( Position, NewBoard ) :- current( B ), play_in_given_place( x, B, NewBoard, Position ), abolish( current/1 ), asserta( current( NewBoard ) ). computer_move( NewBoard ) :- current( B ), rule( o, B, NewBoard ), abolish( current/1 ), asserta( current( NewBoard ) ). % COMPUTER'S MOVE % --------------- % Rule 1: if you can win in this move do so. % Rule 2: if the other player can win in the next move stop him. % Rule 3: now for each possible choice of square, % count 1 for a potential win not yet started. % count 2 for a potential win with one sq. already used.