Prolog 人工智能语言中文论坛---打造优质Prolog学习交流园地

一个供Prolog爱好者学习与交流的地方


您没有登录。 请登录注册

我自己用swi-prolog写的井字游戏

浏览上一个主题 浏览下一个主题 向下  留言 [第1页/共1页]

1 我自己用swi-prolog写的井字游戏 于 周六 三月 10, 2012 3:24 pm

下面的代码是我自己用swi-prolog写的井字游戏程序。
井字游戏就是9个格子,两个玩家轮流,先行者每次挑一个格子画X(另一人画O),谁先连成一条直线就算谁赢。
此程序还有一个功能,就是当你不知道下哪里好时,可以输入h让电脑帮你算出最佳选择。

代码:
%输入Start开始游戏,初始化9个格子,以-代表空格,X代表玩家,O代表电脑。
start:-
  initializecells(9,'-',R,[]), loop(R).
 
%重覆一直做玩家输入、电脑输入、玩家输入……
loop(R) :-
  repeat, write('Now the status is:'),nl,
  length(R,Num), printcells(R,Num),
  write('Please choose your cell(Enter a number between 1-9, Enter h or help):'),nl,
  read(Input), need_help(Input,R), writecell(Input,'X',R,R1,[]),
  not(game_is_over(R1)),
  write('Then I choose '),
  best_strategy(I,'O',R1),
  write(I), writecell(I,'O',R1,R2,[]),nl,
  not(game_is_over(R2)), !, loop(R2).
loop(_).

%当玩家输入h时,电脑会帮其计算出走每步的得分,建议最优选择。
need_help(Input,M) :- Input = h -> best_strategy(I,'X',M), write('For you the best strategy is to choose '), write(I), nl, fail ; true.


%判断游戏是否结束。两种情况代表游戏结束:一是有玩家连成一条直线,二是9个格子皆被占满。
game_is_over(M) :- gameover(M), nl, printcells(M,9), write('Game Over.'), nl,nl, abort.
gameover(M) :- in_line(S,M),(S = 'O'; S = 'X'), write('The Winner is '), (S = 'O' -> write('ME!') ; write('YOU!')), nl, !.
gameover(M) :- countif(M,'-',N), N = 0, write('No Winner!'), nl, !.

%玩家或电脑选择一个格子时,由此句来执行。
writecell(I,S,M) --> {nth1(I,M,'-'), replace(M,I,S,After_write)}, After_write.

%电脑模拟两人互动的过程。以下3句分别代表3个原则:
% 1. 若没格子了,以-当传回值,代表没输没赢。
% 2. 若选择某个格子,可以连成一直线,那必须选择该格子,并且传回X或O代表谁赢了。
% 3. 如果前两项皆不成立,则穷举所有的可能走法。
play_to_end(I,S,Winner,M):- countif(M,'-',N), N = 0, Winner = '-',!.
play_to_end(I,S,Winner,M):- will_in_line(I,S,M), Winner = S,!.
play_to_end(I,S,Winner,M):- writecell(I,S,M,M1,[]),rival(S,Sr),play_to_end(I1,Sr,Winner,M1).

%对某一个走法,算出其得分。当然,算分方式见仁见智了。
strategy_score(I,S,Score,M):-
  bagof(Winner, play_to_end(I,S,Winner,M), Set),
  countif(Set,S,Win), countif(Set,'-',Draw),length(Set,All), Score is round((Win + 0.1 * Draw)/ All * 10000) / 100,
  (S = 'X' -> write('If you choose '),write(I),write(', the score is '), write(Score), write('.'), nl ; true ).

%选出得分最高的策略
best_strategy(I,S,V) :-
  bagof([I,Score],strategy_score(I,S,Score,V),Result),
  nthlv2(2,Result,S1), max_list(S1,Max), nth1(Index,S1,Max),
  nthlv2(1,Result,I1), nth1(Index,I1,I).

%传回对手的标志
rival(S,Sr):- S = 'O' -> Sr = 'X' ; Sr = 'O'.

%判断是否排成一直线
will_in_line(I,S,M):- writecell(I,S,M,M1,[]), in_line(S,M1).

in_line(S,M):- M = [S,S,S,_,_,_,_,_,_],!.
in_line(S,M):- M = [_,_,_,S,S,S,_,_,_],!.
in_line(S,M):- M = [_,_,_,_,_,_,S,S,S],!.
in_line(S,M):- M = [S,_,_,S,_,_,S,_,_],!.
in_line(S,M):- M = [_,S,_,_,S,_,_,S,_],!.
in_line(S,M):- M = [_,_,S,_,_,S,_,_,S],!.
in_line(S,M):- M = [S,_,_,_,S,_,_,_,S],!.
in_line(S,M):- M = [_,_,S,_,S,_,S,_,_],!.

% 印出目前的状态,空的格子以-代替。
printcells(M,Num):-
  Num1 is 10 - Num, Num1 =< 9,
  ( nth1(Num1,M,S), write(S), mod(Num1,3) =:=0 , nl , fail;
    Num2 is Num -1, printcells(M,Num2)
  ),!.
printcells(_,_).

%以符号S初始化Num个格子
initializecells(Num,S) --> {Num >0}, [S], {Num1 is Num -1}, initializecells(Num1,S),!.
initializecells(NUm,S) --> [].

% 计算List(L)中某原子(S)的个数(N)
countif(L,S,N):- bagof(I,nth1(I,L,S),R), length(R,N),!.
countif(L,S,N):- N = 0.

% nthlv2用来取出第二层List的某一项,比如想取出[[a,t,m],[k,e,y],[p,a,s]]中每个List的第3项,
% 则令 A = [[a,t,m],[k,e,y],[p,a,s]], 使用nthlv2(3,A,B), 则 传回 B = [m,y,s]。
listlv2(A,L,S):- nth1(_,L,L2),nth1(A,L2,S).
nthlv2(A,L,R) :- bagof(S,listlv2(A,L,S),R).

%将List中的第Index项以某个原子(With)替换掉,并将结果输出至ListOut
replace(List, Index, With, ListOut) :-
  Idx is Index - 1, length(Before,Idx),
  append(Before, [_Discard|Rest], List),
  append(Before, [With|Rest], ListOut).



由Mercury Liao于周四 十月 25, 2012 11:30 am进行了最后一次编辑,总共编辑了1次

查阅用户资料 http://prolog.longluntan.net

2 我也追著寫自己一套井字遊戲 于 周一 十月 01, 2012 5:51 pm

各位好。我也趕著寫一個井字遊戲,不過只寫了一半,一個外殼而已。我不懂玩井字遊戲,於是主要參考英文維基百科Tic-tac-toe條目寫下這段代碼。

代码:

%%%------------------定義面---------------------
%%%盤面定義: . 是空格, o x 是棋子
place(.).
player(o).
player(x).
opponent(A, B) :-
   player(A), player(B), A \= B.

%%% 求開始的棋面。每個棋格旁邊都帶有一個數字,代表格子號碼。
lattice([S/1,S/2,S/3,S/4,S/5,S/6,S/7,S/8,S/9]) :- place(S).

%%% parts(-Lattice, +Row1, +Row2, +Row3, +Line1, +Line2, +Line3,
%%%                +Dia1, +Dia2)
%%% 將棋面的八條直線取出,較容易匹配每一條直線的狀態。
parts([A,B,C,D,E,F,G,H,I], R1, R2, R3, L1, L2, L3, D1, D2) :-
   R1 = [A,B,C], R2 = [D,E,F], R3 = [G,H,I],
   L1 = [A,D,G], L2 = [B,E,H], L3 = [C,F,I],
   D1 = [A,E,I], D2 = [C,E,G].

%%% move(-Lattice, -Symbol, -CellNumber, +Lattice1)
%%% 由前一個棋盤Lattice,將棋子符號Symbol填入指定棋格位置CellNumber,可得新棋盤Lattice1
move([], _, _, []).
move([_/N|T], S, N, [S/N|T]).
move([H/N|T], S, N1, [H/N|T1]) :- N =\= N1, !, move(T, S, N1, T1).

%%%------------------策略面---------------------

%%% pattern(-Line, -Symbol, +Pattern/Nth)
%%% pattern(-Line, -symbol, -Symbol, +Pattern/Nth)
%%% 二種匹配判定。 pattern/4 判定二種符號 (可能是棋子或空位) 的關係。
pattern([S/N,_,_], S, p1/N).
pattern([_,S/N,_], S, p2/N).
pattern([_,_,S/N], S, p3/N).
pattern([S/_,S/_,S/_], S, p4).
pattern(_, _, p5).
pattern([S/N,T/_,T/_], S, T, p6/N).
pattern([T/_,S/N,T/_], S, T, p7/N).
pattern([T/_,T/_,S/N], S, T, p8/N).

%%% any_pattern/3 , any_pattern/4 檢查二個以上的直線是否能匹配到某一個pattern。
any_pattern([Line|_], Player, Pattern) :-
   pattern(Line, Player, Pattern).
any_pattern([_|Tail], Player, Pattern) :-
   any_pattern(Tail, Player, Pattern).
any_pattern([Line|_], Sym1, Sym2, Pattern) :-
   pattern(Line, Sym1, Sym2, Pattern).
any_pattern([_|Tail], Sym1, Sym2, Pattern) :-
   any_pattern(Tail, Sym1, Sym2, Pattern).

%%% 策略 0 :獲勝檢查。
win(Lattice, Player) :-
   parts(Lattice, R1, R2, R3, L1, L2, L3, D1, D2),
   any_pattern([R1,R2,R3,L1,L2,L3,D1,D2], Player, p4).

%%% 策略 1 :只差一步即可成功的策略。
strategy(Lattice, Player, N, Lattice1) :-
   parts(Lattice, R1, R2, R3, L1, L2, L3, D1, D2),
   place(Place),
   any_pattern([R1,R2,R3,L1,L2,L3,D1,D2], Place, Player, Pattern/N), !,
   (Pattern = p6; Pattern = p7; Pattern = p8),
   move(Lattice, Player, N, Lattice1).

%%% 策略 2 :攔阻策略。
strategy(Lattice, Player, N, Lattice1) :-
   parts(Lattice, R1, R2, R3, L1, L2, L3, D1, D2),
   opponent(Player, Enemy),
   place(Place),
   any_pattern([R1,R2,R3,L1,L2,L3,D1,D2], Place, Enemy, Pattern/N), !,
   (Pattern = p6; Pattern = p7; Pattern = p8),
   move(Lattice, Player, N, Lattice1).

%%%------------------互動面---------------------

%%% 電腦方的提示
my_turn(N) :- write('My turn: '), write(N), write('.'), nl, nl.

%%% 人類玩家方的提示輸入
input(X) :- write('Your turn: '), read(X).
input_digit_without_0(X) :-
   input(X),
   member(X, [1,2,3,4,5,6,7,8,9]), !.
input_digit_without_0(X) :-
   write('Input must be [1-9].'), nl,
   input_digit_without_0(X).

%%% 視覺顯示
show_off(Lattice) :-
   parts(Lattice, R1, R2, R3, _, _, _, _, _),
   show_off_line(R1),
   show_off_line(R2),
   show_off_line(R3).
show_off_line([]) :- write(']'), nl.
show_off_line([H/_|T]) :- write('['), write(H), show_off_line(T).

%%% 人類玩家方的回合:輸入格號,產生新棋面,顯示棋面,然後判斷若獲勝則中斷。
turn1(Lattice, Player, Lattice1) :-
   input_digit_without_0(N),
   move(Lattice, Player, N, Lattice1),
   show_off(Lattice1),
   (win(Lattice1, Player) -> write('You win!'), nl, fail;
       true).

%%% 電腦方的回合:執行策略,假如策略成功則顯示棋面,否則投降。
turn2(Lattice, Player, Lattice1) :-
   strategy(Lattice, Player, N, Lattice1), !,
   my_turn(N),
   show_off(Lattice1),
   (win(Lattice1, Player) -> write('My pride!'), nl, fail;
       true).
turn2(_, _, _) :-
   my_turn('Surrender').

%%% 遊戲開始點:由查找 game/0 開始。
game :-
   lattice(Lattice),
   player(O), opponent(O, X),
   show_off(Lattice),
   turn1(Lattice, O, Lattice1),
   turn2(Lattice1, X, Lattice2), !.

查阅用户资料 http://yauhsien.wordpress.com

3 回复: 我自己用swi-prolog写的井字游戏 于 周五 一月 04, 2013 3:22 am

楼主可以录制个视频哈。内容呢,比如,怎么搭建环境,导入demo,运行应用。调试程序

查阅用户资料

4 回复: 我自己用swi-prolog写的井字游戏 于 周五 一月 04, 2013 5:37 am

小马过河 写道::楼主可以录制个视频哈。内容呢,比如,怎么搭建环境,导入demo,运行应用。调试程序


咦~~我怎么没想到呢!对哈可以用视频。

很好的建议,感谢!

查阅用户资料 http://prolog.longluntan.net

5 回复: 我自己用swi-prolog写的井字游戏 于 周六 一月 05, 2013 2:12 am

亲,这游戏这么玩啊。我把代码考出去了。然后运行。可是出错啊

:-:-start.
Now the status is:
?
ERROR: printcells/2: Undefined procedure: nth1/3
Exception: (9) nth1(1, [-, -, -, -, -, -, -, -|...], _G202)
:-start.
? ?

查阅用户资料

6 回复: 我自己用swi-prolog写的井字游戏 于 周六 一月 05, 2013 6:12 am

小马过河 写道::亲,这游戏这么玩啊。我把代码考出去了。然后运行。可是出错啊

:-:-start.
Now the status is:
?
ERROR: printcells/2: Undefined procedure: nth1/3
Exception: (9) nth1(1, [-, -, -, -, -, -, -, -|...], _G202)
:-start.
? ?

你用的是SWI-Prolog吗?
我用SWI-Prolog是可以的。

查阅用户资料 http://prolog.longluntan.net

7 回复: 我自己用swi-prolog写的井字游戏 于 周六 一月 05, 2013 9:09 am

我一输入 start.
就出现后面的错误了
start 后面要不要加参数什么的

查阅用户资料

浏览上一个主题 浏览下一个主题 返回页首  留言 [第1页/共1页]

您在这个论坛的权限:
不能在这个论坛回复主题