我正在构建一个简单的井字游戏,我已经完全用网格砸墙了。
每次玩家做出选择时,我都希望“投入@grid”(包括玩家输入)。我该如何实现?我几乎是Ruby的初学者,以前从未开发过任何游戏。非常感谢您的任何帮助!
我尝试制作两个不同的网格(grid和grid_with_markers),但不知道从那里去哪里。回首过去,拥有两个网格似乎也不是一个好主意。我还尝试过使用哈希(marker_positions),但与数组相比似乎过于复杂。
这是网格
def initialize
@possible_choice = [1,2,3,4,5,6,7,8,9]
@marker_positions = [1,2,3,4,5,6,7,8,9]
@grid = "
|----|----|----|
| #{@marker_positions[0]} | #{@marker_positions[1]} | #{@marker_positions[2]} |
|----|----|----|
| #{@marker_positions[3]} | #{@marker_positions[4]} | #{@marker_positions[5]} |
|----|----|----|
| #{@marker_positions[6]} | #{@marker_positions[7]} | #{@marker_positions[8]} |
|----|----|----|
"
end
我想使用add_markers方法来显示带有用户输入的网格。因此,当玩家选择数字1时,@ marker_positions [0]将被替换为“ X”(或“ O”)。数字4将替换@marker_positions [3]等。
编辑:由于我实际上想用 Strings (“ X”或“ O”)替换@marker_positions数组的元素,因此我意识到这篇文章的标题具有误导性。但是,替换的元素是根据用户输入(即player_one和player_two数组)选择的。
def add_markers
puts @grid
end
这是player_one_turn方法
def player_one_turn()
puts "Player One, make your choice:"
p @possible_choice
add_markers
@@player_one << @possible_choice.delete(gets.chomp.to_i)
p "Player One has chosen: #{@@player_one}"
end
这是我的整个tictactoe.rb文件。
class Grid
WINNING_COMBOS = [
[1,2,3],[4,5,6],[7,8,9],
[1,4,7],[2,5,8],[3,6,9],
[1,5,9],[3,5,7]
]
attr_accessor :possible_choice
attr_accessor :marker_positions
attr_accessor :grid
def initialize
@possible_choice = [1,2,3,4,5,6,7,8,9]
@marker_positions = [1,2,3,4,5,6,7,8,9]
@grid = "
|----|----|----|
| #{@marker_positions[0]} | #{@marker_positions[1]} | #{@marker_positions[2]} |
|----|----|----|
| #{@marker_positions[3]} | #{@marker_positions[4]} | #{@marker_positions[5]} |
|----|----|----|
| #{@marker_positions[6]} | #{@marker_positions[7]} | #{@marker_positions[8]} |
|----|----|----|
"
end
def add_markers
puts @grid
end
end
class Game < Grid
@@player_one = Array.new
@@player_two = Array.new
def game
puts
puts "*** This is a tic-tac-toe game for two human players. ***"
puts
loop do
player_one_turn()
puts
if has_won?(@@player_one)
puts "The game has ended. Player One has won!"
puts
return
end
break if @@player_one.length == 5 || @@player_one.include?(nil)
player_two_turn()
puts
if has_won?(@@player_two)
puts "The game has ended. Player Two has won!"
puts
return
end
end
end
def player_one_turn()
puts "Player One, make your choice:"
p @possible_choice
add_markers
@@player_one << @possible_choice.delete(gets.chomp.to_i)
p "Player One has chosen: #{@@player_one}"
end
def player_two_turn()
puts "Player Two, make your choice:"
p @possible_choice
add_markers
@@player_two << @possible_choice.delete(gets.chomp.to_i)
p "Player Two has chosen: #{@@player_two}"
end
def has_won?(player)
WINNING_COMBOS.any? { |combo| (player & combo).size == combo.size}
end
end
new_game = Game.new
new_game.game
(我知道这不是很干净。感谢您抽出宝贵的时间在这里阅读所有内容。)
答案 0 :(得分:0)
因此,当玩家选择数字1时,@ marker_positions [0]将替换为“ X”
您只需要阅读用户输入:(示例显示了玩家输入 1 的结果)
input = gets.to_i
#=> 1
并替换相应的数组元素:(我们必须减去1,因为数组是从零开始的)
@marker_positions[input - 1] = 'X'
在网格字符串中使用数组值是另一个问题。创建字符串时,通过#{...}
进行插值,恰好是在将其分配给@grid
中的initialize
之前。这意味着当插值值之后发生更改时,该字符串不会被更新–除非再次插值,否则字符串将保持不变:
x = 'foo'
@grid = "hello #{x}"
#=> "Hello foo"
x = 'bar'
@grid
#=> "Hello foo" <- doesn't change
@grid = "hello #{x}"
#=> "Hello bar"
要在每次调用add_markers
时获得一个“新鲜”的网格,您可以将字符串插值代码从initialize
移至该方法中:(代替"..."
,我正在使用<<~EOD ... EOD
,一个“蠕动的” heredoc,它会忽略前导空白)
class Grid
attr_accessor :marker_positions
def initialize
@marker_positions = [1, 2, 3, 4, 5, 6, 7, 8, 9]
end
def add_markers
puts <<~EOD
|----|----|----|
| #{@marker_positions[0]} | #{@marker_positions[1]} | #{@marker_positions[2]} |
|----|----|----|
| #{@marker_positions[3]} | #{@marker_positions[4]} | #{@marker_positions[5]} |
|----|----|----|
| #{@marker_positions[6]} | #{@marker_positions[7]} | #{@marker_positions[8]} |
|----|----|----|
EOD
end
end
这将导致:(#
不是实际输出的一部分)
grid = Grid.new
grid.add_markers
#|----|----|----|
#| 1 | 2 | 3 |
#|----|----|----|
#| 4 | 5 | 6 |
#|----|----|----|
#| 7 | 8 | 9 |
#|----|----|----|
grid.marker_positions[0] = 'X'
grid.marker_positions[4] = 'X'
grid.marker_positions[8] = 'X'
grid.add_markers
#|----|----|----|
#| X | 2 | 3 |
#|----|----|----|
#| 4 | X | 6 |
#|----|----|----|
#| 7 | 8 | X |
#|----|----|----|
另一个(也许更干净)的选项是定义一个模板,该模板的占位符在呈现模板时被其实际(或当前)值替换:
class Grid
TEMPLATE = <<~EOD.freeze
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ 9 │
└───┴───┴───┘
EOD
attr_accessor :marker_positions
def initialize
@marker_positions = [1, 2, 3, 4, 5, 6, 7, 8, 9]
end
def render
puts TEMPLATE.gsub(/[1-9]/) { |d| @marker_positions[d.to_i - 1] }
end
end
gsub
在模板字符串中扫描从1
到9
的数字,并将每次出现的d
替换为块的结果,该结果仅从数组中选择相应的值。 / p>
用法:
grid = Grid.new
grid.render
#┌───┬───┬───┐
#│ 1 │ 2 │ 3 │
#├───┼───┼───┤
#│ 4 │ 5 │ 6 │
#├───┼───┼───┤
#│ 7 │ 8 │ 9 │
#└───┴───┴───┘
grid.marker_positions[0] = 'X'
grid.marker_positions[4] = 'X'
grid.marker_positions[8] = 'X'
grid.render
#┌───┬───┬───┐
#│ X │ 2 │ 3 │
#├───┼───┼───┤
#│ 4 │ X │ 6 │
#├───┼───┼───┤
#│ 7 │ 8 │ X │
#└───┴───┴───┘
Unicode box-drawing characters仅用于演示目的,它将与ASCII字符一起正常工作。
基于上述内容,一个非常基本的游戏循环可能看起来像这样:
grid = Grid.new
%w[X O].cycle do |marker|
grid.render
print "Player #{marker}: "
input = gets.to_i
grid.marker_positions[input - 1] = marker
# TODO: break if player wins
end
"X"
和"O"
之间的代码cycle
,以及每次迭代:
marker_positions
如果玩家获胜,则缺少break
循环的额外步骤。可能还有一些逻辑可以验证输入(防止玩家覆盖已经占据的位置,检查输入是否在1-9之内,等等)。
希望这会有所帮助。
最后一点:假设用户通过计算机键盘输入输入,则可能需要垂直翻转网格以匹配numeric keypad的布局。