我正在尝试制作一个井字游戏。我只列出了目前为止所获得的相关课程和方法的代码:
class Box
attr_reader :name, :row, :column
attr_accessor :is_marked, :contents
def initialize(name, row, column, is_marked=false, contents)
@name = name
@row = row
@column = column
@is_marked = is_marked
@contents = contents
end
def self.display_box
print '|#{contents}|'
end
end
#generate box instances
(1..9).each do |i|
if i > 3
col = i % 3
else
col = i
end
box#{i} = Box.new('box#{i}', (i/3).ceil, col, false, '_')
end
board = [[box1, box2, box3], [box4, box5, box6], [box7, box8, box9]]
def display_board
box1.display_box; box2.display_box; box3.display_box; print '\n'
box4.display_box; box5.display_box; box6.display_box; print '\n'
box7.display_box; box8.display_box; box9.display_box; print '\n'
end
display_board
我无法弄清楚为什么创建我的类的实例会引发错误。错误是:
undefined local variable or method `box1' for <Context:0x000000024df8a8>
(repl):44:in display_board'
(repl):61:in initialize'
我尝试在display_box
方法中使用和不使用'self'运行它,同样的错误。
答案 0 :(得分:3)
问题在于这一行。
box#{i} = Box.new('box#{i}', (i/3).ceil, col, false, '_')
您似乎正在尝试创建一组名为box1
,box2
的变量,...在红宝石中可能还有其他方法可以做到这一点,但是那个&#39;不是吗你可以用eval
来做,但你不想这样做。正如您从其余代码中可以看到的那样,处理大量变量就很烦人了。
而是制作一个盒子数组。
boxes[i] = Box.new("box#{i}", (i/3).ceil, col, false, '_')
您需要在循环之前声明boxes = []
。
然后使用范围从该列表创建您的电路板。
board = [boxes[1..3], boxes[4..6], boxes[7..9]]
display_board
变得不那么重复了。
def display_board(board)
board.each { |row|
row.each { |box|
box.display_box
}
puts "\n"
}
end
最后,如果要在字符串中插入变量,则必须使用"
。例如,在display_box
。
def display_box
print "|#{contents}|"
end
答案 1 :(得分:2)
这是一个错误:
box#{i}
您可以插入字符串(和正则表达式),但变量名称不是a 串。字符串有引号。
即使您可以插入变量名称,也绝不会这样做:
box#{i} = Box.new('box#{i}', (i/3).ceil, col, false, '_')
相反,您将创建一个Array并将实例添加到Array:
boxes = []
(1..9).each do |i|
if i > 3
col = i % 3
else
col = i
end
boxes << Box.new('box#{i}', (i/3).ceil, col, false, '_')
end
然后,您的实例的名称是方框[0],方框[1]等
您还需要知道def创建了一个新范围,并且def内部的变量无法在def中看到。因此,在def内部无法看到在def外部创建的任何box1,box2等变量。您需要做的是将Array框传递给display_board()方法,如下所示:
def display_board(board_boxes)
...
end
display_board(boxes)
Ruby然后将您的方法调用和方法标题排列如下:
display_board(boxes)
|
V
def display_board(board_boxes)
并将数组分配给名为board_boxes的变量:
board_boxes = boxes
然后在def中,board_boxes将是包含框的数组。
不要使用半冒号在一行上组合代码行。使用数组存储盒子实例时,可以显示如下框:
board_boxes.each do |box|
box.display_box
end
如果您想在每三个方框后打印换行符,可以执行以下操作:
count = 1
board_boxes.each do |box|
box.display_box
print "\n" if count%3 == 0
count += 1
end
答案 2 :(得分:0)
正如其他用户所提到的,你可以创建一个数组来保存Box的实例。
如果您已设置使用变量,则可以使用instance_variable_set,尽管使用数组可能更好。有关详细信息,请参阅here。
使用instance_variable_set:
class Box
attr_reader :name, :row, :column
attr_accessor :is_marked, :contents
def initialize(name, row, column, is_marked=false, contents)
@name = name
@row = row
@column = column
@is_marked = is_marked
@contents = contents
end
def display_box
print '|#{contents}|'
end
end
#generate box instances
(1..9).each do |i|
if i > 3
col = i % 3
else
col = i
end
instance_variable_set("@box#{i}", Box.new('box#{i}', (i/3).ceil, col, false, '_'))
end
board = [[@box1, @box2, @box3], [@box4, @box5, @box6], [@box7, @box8, @box9]]
def display_board
@box1.display_box; @box2.display_box; @box3.display_box; print '\n'
@box4.display_box; @box5.display_box; @box6.display_box; print '\n'
@box7.display_box; @box8.display_box; @box9.display_box; print '\n'
end
display_board
使用数组:
class Box
attr_reader :name, :row, :column
attr_accessor :is_marked, :contents
def initialize(name, row, column, is_marked=false, contents)
@name = name
@row = row
@column = column
@is_marked = is_marked
@contents = contents
end
def display_box
print '|#{contents}|'
end
end
#generate box instances
arr = [];
(1..9).each do |i|
if i > 3
col = i % 3
else
col = i
end
name = 'box' + i.to_s
arr.push(Box.new(name, (i/3).ceil, col, false, '_'))
end
board = [[arr[0], arr[1], arr[2]], [arr[3], arr[4], arr[5]], [arr[6], arr[7], arr[8]]]
def display_board(arr)
arr[0].display_box; arr[1].display_box; arr[2].display_box; print '\n'
arr[3].display_box; arr[4].display_box; arr[5].display_box; print '\n'
arr[6].display_box; arr[7].display_box; arr[8].display_box; print '\n'
end
display_board(arr)
答案 3 :(得分:0)
您获得undefined local variable or method `box1'
,因为未定义box1
。
显然,您希望此行创建box1
:
box#{i} = ...
但实际上就是这样:
box
因为#
开始发表评论。
双引号strings允许插值,但不能像这样插入变量名。
动态创建变量几乎总是一个坏主意。如果您有大量或不同数量的值,则应将它们存储在一个集合中 - Array
,Hash
或自定义值。
设置框非常复杂,因为您要将名称,行和列传递给每个框。一个盒子真的必须知道它在董事会中的位置吗?想想其他对象是更大结构的一部分:数组元素不知道它的索引(数组没有),字符不知道它的位置(字符串没有),哈希值不知道它的键(哈希确实)。
让我们从Box
中删除这些额外的属性,让它只存储其内容:
class Box
attr_accessor :content
def initialize
@content = '_'
end
def marked?
content != '_'
end
end
b = Box.new #=> #<Box:0x007fab7a9f58a8 @content="_">
b.content #=> "_"
b.marked? #=> false
b.content = 'X'
b.marked? #=> true
要创建一个3×3的盒子数组,我们可以写:(我已从输出中删除了对象ID)
board = Array.new(3) { Array.new(3) { Box.new } }
#=> [[#<Box @content="_">, #<Box @content="_">, #<Box @content="_">],
# [#<Box @content="_">, #<Box @content="_">, #<Box @content="_">],
# [#<Box @content="_">, #<Box @content="_">, #<Box @content="_">]]>
可以通过以下方式访问单个框:(索引从零开始)
board[0][0] #=> #<Box @content="_">
但由于board
是如此重要的对象,我会编写一个自定义类:
class Board
def initialize
@boxes = Array.new(3) { Array.new(3) { Box.new } }
end
def box(x, y)
@boxes[x][y]
end
end
该类不公开@boxes
,因为我们不希望更改数组。它提供了一种方法Board#box
,它返回给定x
和y
坐标的框。
在我看来,Box
和Board
都不应该包含处理格式化的代码,所以让我们编写一个小实用程序类来处理这一部分:
class BoardFormatter
def draw(board)
template.gsub(/[0-8]/) do |n|
x, y = n.to_i.divmod 3
board.box(x, y).content
end
end
def template
<<-STR
0 | 1 | 2
---+---+---
3 | 4 | 5
---+---+---
6 | 7 | 8
STR
end
end
BoardFormatter#template
返回董事会的字符串。您之前可能没有看到<<-
:它被称为heredoc并在给定的STR
分隔符之间创建多行字符串:
formatter = BoardFormatter.new
formatter.template #=> " 0 | 1 | 2\n---+---+---\n 3 | 4 | 5\n---+---+---\n 6 | 7 | 8\n"
BoardFormatter#draw
使用String#gsub
将每个数字([0-8]
)替换为相应的“内容”。
divmod
是此处的关键,它将a / b
和a % b
作为数组返回。因为我们使用从零开始的索引,所以这些只是第n个框的框坐标:
0.divmod(3) #=> [0, 0]
1.divmod(3) #=> [0, 1]
2.divmod(3) #=> [0, 2]
3.divmod(3) #=> [1, 0]
4.divmod(3) #=> [1, 1]
5.divmod(3) #=> [1, 2]
6.divmod(3) #=> [2, 0]
7.divmod(3) #=> [2, 1]
8.divmod(3) #=> [2, 2]
board = Board.new
board.box(0, 2).content = 'X'
board.box(0, 0).content = 'O'
board.box(2, 0).content = 'X'
board.box(1, 1).content = 'O'
formatter = BoardFormatter.new
puts formatter.draw(board)
输出:
O | _ | X
---+---+---
_ | O | _
---+---+---
X | _ | _
玩家应该在玩游戏时输入一个基于索引的指数。处理用户输入时应处理此转换。在核心代码中,您应该始终使用从零开始的索引,就像Ruby一样。