SWIPROLOG实现23根火柴游戏:求电脑必胜策略及代码优化
23根火柴游戏:电脑必胜策略与代码优化
必胜策略分析
游戏核心规则:桌上初始有23根火柴,两人轮流取1-3根,取到倒数第二根火柴的玩家输掉游戏(即取完后剩余1根时,取火柴的玩家因取了倒数第二根输掉;取完后剩余0根时,取火柴的玩家因取了包含倒数第二根的所有火柴,同样输掉)。
要让电脑近乎必胜,核心是迫使玩家进入必败态:当轮到玩家行动时,桌上火柴数为2、6、10、14、18、22(满足N ≡ 2 mod 4)。此时无论玩家取1、2、3根,电脑都取4-M根(M为玩家取的数量),让下一轮剩余火柴数仍保持≡2 mod4,最终玩家会面对2根火柴,无论怎么取都会输掉。
电脑作为先手时,第一次取1根,剩余22根(必败态),之后每轮保持两人取火柴总数为4,即可牢牢掌控游戏节奏。
优化后的完整代码
% 启动游戏:初始23根火柴,电脑先手 joc :- joc(23, calculator, in_progress). % 玩家回合:游戏结束状态 joc(N, player, finished) :- nl, write('There are '), write(N), write(' matches on the table.'), nl, write('The game has ended.'), nl. % 玩家回合:进行中状态 joc(N, player, in_progress) :- nl, write('There are '), write(N), write(' matches on the table.'), nl, write('It is your turn. Enter number of matches to take (1-3): '), nl, read(M), (valid_move(M, N) -> N1 is N - M, write('You took '), write(M), write(' matches.'), nl, (check_loss(N1) -> write('Game over! You took the second last match, you lose.'), nl, joc(N1, calculator, finished) ; joc(N1, calculator, in_progress) ) ; write('Invalid move! Please take 1-3 matches, and no more than available.'), nl, joc(N, player, in_progress) ). % 电脑回合:游戏结束状态 joc(N, calculator, finished) :- nl, write('There are '), write(N), write(' matches on the table.'), nl, write('The game has ended.'), nl. % 电脑回合:进行中状态 joc(N, calculator, in_progress) :- nl, write('There are '), write(N), write(' matches on the table.'), nl, write('It is the computer\'s turn.'), nl, optimal_move(N, M), N1 is N - M, write('The computer took '), write(M), write(' matches.'), nl, (check_loss(N1) -> write('Game over! The computer took the second last match, you win.'), nl, joc(N1, player, finished) ; joc(N1, player, in_progress) ). % 验证移动合法性:取1-3根,且不超过剩余火柴数 valid_move(M, N) :- M >= 1, M =< 3, M =< N. % 判断当前取火柴的玩家是否输掉:取完后剩余≤1根,说明取了倒数第二根 check_loss(N1) :- N1 =< 1. % 电脑最优移动策略:让剩余火柴数保持为必败态(≡2 mod4) optimal_move(N, M) :- Rem is N mod 4, (Rem =:= 0 -> M = 2; % N=4→取2剩2,N=8→取2剩6,以此类推 Rem =:= 1 -> M = 3; % N=5→取3剩2,N=9→取3剩6,以此类推 Rem =:= 2 -> between(1, 3, M), valid_move(M, N); % 已在必败态,随机取合法值 Rem =:= 3 -> M = 1). % N=3→取1剩2,N=7→取1剩6,以此类推
代码关键说明
- 回合逻辑:分玩家和电脑两个分支处理,清晰区分游戏结束与进行中状态,避免原代码的状态判断混乱。
- 胜负判定:通过
check_loss/1直接判断取完火柴后的剩余数量,逻辑更直观准确。 - 最优策略:
optimal_move/2根据剩余火柴数的模4结果计算移动量,确保每一步都将玩家推向必败态,解决了原代码中剩3根时电脑错误取2根的问题。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

