我想知道是否有人能帮我解决这个问题…我肯定这是那种拍额头的东西…但我不知道怎么把这一点擦干。
我正在做一个tic-tac-toe项目作为学习练习。实际上我已经完成了,但是有一点让我不安,因为它看起来很笨拙。当我为电脑的移动做人工智能的时候这样做的方式是,测试八个条件中的每一个,寻找可能的移动。查看第一个条件;如果它导致移动,则返回该移动并跳过其余条件。如果八个条件中没有一个引起移动,则游戏结束(棋盘已满)。
好吧,条件非常复杂,它们都需要单独的方法。所以,要执行逻辑,我说的是简单的1.times
(sigh),然后在一个只执行一次的块中,逐个地执行这些方法。在检查方法是否返回move之后,我会说break if move
,即从块中跳出很明显,我不应该在每个项目之后使用break if move
违反干法我想使用if...elsif...
或switch
语句,但这不起作用,因为所有方法都必须经过,直到其中一个返回move,并且这些方法不仅返回true或false;它们实际上返回move的索引号或false
。
(unless skip_rule == n
位是一种压制人工智能的方法;如果玩家希望人工智能能够被击败,则另一种方法选择要跳过的规则。)
如果您想深入了解,请检查第96+行的代码:https://github.com/globewalldesk/tictactoe2/blob/master/lib/board.rb
# Test each set of conditions, until a move is found
move = false
1.times do
# Win: If you have two in a row, play the third to get three in a row.
# puts "Trying 1"
move, length = are_there_two_tokens_in_a_row(@ctoken) unless skip_rule == 0
break if move # skip to end if move is found
# Block: If the opponent has two in a row, play the third to block them.
# puts "Trying 2"
move, length = are_there_two_tokens_in_a_row(@ptoken) unless skip_rule == 1
break if move
# Fork: Create an opportunity where you can win in two ways (a fork).
# puts "Trying 3"
move = discover_fork(@ctoken) unless skip_rule == 2
break if move
# Block Opponent's Fork: If opponent can create fork, block that fork.
# puts "Trying 4"
move = discover_fork(@ptoken) unless skip_rule == 3
break if move
# Center: Play the center.
# puts "Trying 5"
unless skip_rule == 4
move = 4 if @spaces[4].c == " " # if the center is open, move there
end
break if move
# Opposite Corner: If the opponent is in the corner, play the opposite corner.
# puts "Trying 6"
move = try_opposite_corner unless skip_rule == 5
break if move
# Empty Corner: Play an empty corner.
# puts "Trying 7"
move = try_empty_corner
break if move
# Empty Side: Play an empty side.
# puts "Trying 8"
move = play_empty_side
# If move is still false, game is over!
end # of "do" block
# Make the change to @spaces; this edits the individual space and hence also
# the triads and the board, which use it.
@spaces[move].c = @ctoken if move
end # of computer_moves
更新(12/3/2016):我不太理解下面的建议。但一个离线的朋友看了看这个问题,建议我做一个方法数组,然后重复一遍……这就是我的手撞到我额头的声音我知道有一个相对简单的方法无论如何,在ruby中一个方法数组可能是不可能的,但是可以创建一个包含方法的过程数组。所以我就是这么做的下面是修正/改进的代码:
# Initialize array of procs for each step of algorithm
rules = [
# Win: If you have two in a row, play the third to get three in a row.
Proc.new { are_there_two_tokens_in_a_row(@ctoken) },
# Block: If the opponent has two in a row, play the third to block them.
Proc.new { are_there_two_tokens_in_a_row(@ptoken) },
# Fork: Create an opportunity where you can win in two ways (a fork).
Proc.new { discover_fork(@ctoken) },
# Block Opponent's Fork: If opponent can create fork, block that fork.
Proc.new { discover_fork(@ctoken) },
# Center: Play the center.
Proc.new {
unless skip_rule == 4
move = 4 if @spaces[4].c == " " # if the center is open, move there
end
},
# Opposite Corner: If the opponent is in the corner, play the opposite corner.
Proc.new { try_opposite_corner },
# Empty Corner: Play an empty corner.
Proc.new { try_empty_corner },
# Empty Side: Play an empty side.
Proc.new { play_empty_side }
]
# Iterates over rule procs, and breaks out of iteration when move != false
(0..7).each do |rule_index|
move, length = rules[rule_index].call unless skip_rule == rule_index
break if move
end
漂亮,不是吗?据我所知,这段代码已经干涸了。Here是程序的更新源文件。顺便说一句,这个程序可以被设置为无与伦比的,但用户可以让它“忘记”随机规则,使它可以击败。
感谢所有回复的人下次我将尝试使用Code Review.SE。
最佳答案
只需将1.times
块中的代码重构为自己的方法而不是break if move
使用显式return
运算符,方法相同:return if move
。
关于ruby - 如何在井字游戏中将Ruby代码干燥掉? (使用1次块!),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40797849/