Ruby:如何替换数组中的元素?

时间:2019-05-29 08:33:56

标签: arrays ruby methods

我正在构建一个简单的井字游戏,我已经完全用网格砸墙了。

每次玩家做出选择时,我都希望“投入@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

(我知道这不是很干净。感谢您抽出宝贵的时间在这里阅读所有内容。)

1 个答案:

答案 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在模板字符串中扫描从19的数字,并将每次出现的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的布局。