Gomoku Game in Ruby
I learnt lots about ruby from my last post Game of Life in Ruby, so I have my next try in Ruby, it is Gomoku.
It is still an console game and I use three classes Game
Grid
Cell
, the structure is similar with the @Johan Wentholt 's example code. But of course game rule is different.
Game class
Game class to run the game
class Game
def initialize(width=15)
@width = width
@users = ["A", "B"]
@user_piece = {"A"=>"+", "B"=>"*"}
@user_index = 0
end
def reset
@grid = Grid.new(@width)
end
def start
reset
puts @grid
until @grid.gameover
user = @users[@user_index]
print "Now for user<#{user}>, Enter your move(split by space)[0-#{@width-1}]:"
begin
move = gets.chomp.split.map(&:to_i)
if not @grid.update?(move, @user_piece[user])
puts "Invalid move!!!"
else
switch_user
puts @grid
end
rescue
puts "Invalid move!!!"
end
end
show_result
end
def switch_user
@user_index = (@user_index + 1) % @users.length
end
def show_result
if not @grid.draw
switch_user
print "Game Over the Winner is <#{@users[@user_index]}>"
else
print "Game Over Draw"
end
end
end
Grid class
Grid present is the gomoku game board, keep updating piece on board and whether the game is over
class Grid
attr_reader :gameover, :draw
def initialize(width)
@cells = Array.new(width * width).map { Cell.new }
@grid = @cells.each_slice(width).to_a
@gameover = false
@draw = false
@width = width
assign_cell_neighbours
end
def update?(move, piece)
x, y = move
if x.negative? || x >= @width || y.negative? || y >= @width
return false
end
cell = @grid.dig(x,y)
if not cell.place?(piece)
return false
end
if not full?
@gameover = cell.win?
else
@gameover = true
@draw = true
end
return true
end
def full?
@cells.none?{|cell| cell.empty?}
end
def to_s
@grid.map {|row| row.map(&:to_s).join}.join("n")
end
private
def assign_cell_neighbours
@grid.each_with_index do |row, row_index|
row.each_with_index do |cell, column_index|
Cell::RELATIVE_NEIGHBOUR_COORDINATES.each do |dir, rel_coord|
(rel_row_index, rel_column_index) = rel_coord
neighbour_row_index = row_index
neighbour_column_index = column_index
neighbours =
loop do
neighbour_row_index += rel_row_index
neighbour_column_index += rel_column_index
break if neighbour_row_index.negative? ||
neighbour_column_index.negative? ||
neighbour_row_index >= @width ||
neighbour_column_index >= @width
neighbours << @grid.dig(neighbour_row_index, neighbour_column_index)
end
cell[dir] = neighbours
end
end
end
end
end
Cell class
Cell class keep the cell state and alse judge whether current update on cell lead to a win
class Cell
RELATIVE_NEIGHBOUR_COORDINATES = {
north: [-1, 0].freeze, north_east: [-1, 1].freeze,
east: [0, 1].freeze, south_east: [1, 1].freeze,
south: [1, 0].freeze, south_west: [1, -1].freeze,
west: [0, -1].freeze, north_west: [-1, -1].freeze,
}.freeze
NEIGHBOUR_DIRECTIONS = RELATIVE_NEIGHBOUR_COORDINATES.keys.freeze
PAIR_DIRECTIONS = [[:north, :south].freeze,
[:east, :west].freeze,
[:north_east, :south_west].freeze,
[:north_west, :south_east].freeze].freeze
EMPTY = "."
attr_accessor(*NEIGHBOUR_DIRECTIONS)
def initialize
@cell = EMPTY
end
def empty?
@cell == EMPTY
end
def place?(piece)
if empty? and valid_piece?(piece)
@cell = piece
return true
end
return false
end
def win?
neighbours.compact.select{|x| x>=4}.length > 0
end
def (direction)
validate_direction(direction)
send(direction)
end
def =(direction, neighbour)
validate_direction(direction)
send("#{direction}=", neighbour)
end
def neighbours
PAIR_DIRECTIONS.map{ |directions|
directions.map{|direction|
self[direction].find_index{|neighbour| neighbour.to_s != self.to_s}
}.compact.inject(:+)
}
end
def to_s
@cell
end
def inspect
"<#{self.class} #{@cell}>"
end
private
def validate_direction(direction)
unless NEIGHBOUR_DIRECTIONS.map(&:to_s).include?(direction.to_s)
raise "unsupported direction #{direction}"
end
end
def valid_piece?(piece)
piece != EMPTY
end
end
Full code
#!/usr/bin/ruby
class Cell
RELATIVE_NEIGHBOUR_COORDINATES = {
north: [-1, 0].freeze, north_east: [-1, 1].freeze,
east: [0, 1].freeze, south_east: [1, 1].freeze,
south: [1, 0].freeze, south_west: [1, -1].freeze,
west: [0, -1].freeze, north_west: [-1, -1].freeze,
}.freeze
NEIGHBOUR_DIRECTIONS = RELATIVE_NEIGHBOUR_COORDINATES.keys.freeze
PAIR_DIRECTIONS = [[:north, :south].freeze,
[:east, :west].freeze,
[:north_east, :south_west].freeze,
[:north_west, :south_east].freeze].freeze
EMPTY = "."
attr_accessor(*NEIGHBOUR_DIRECTIONS)
def initialize
@cell = EMPTY
end
def empty?
@cell == EMPTY
end
def place?(piece)
if empty? and valid_piece?(piece)
@cell = piece
return true
end
return false
end
def win?
neighbours.compact.select{|x| x>=4}.length > 0
end
def (direction)
validate_direction(direction)
send(direction)
end
def =(direction, neighbour)
validate_direction(direction)
send("#{direction}=", neighbour)
end
def neighbours
PAIR_DIRECTIONS.map{ |directions|
directions.map{|direction|
self[direction].find_index{|neighbour| neighbour.to_s != self.to_s}
}.compact.inject(:+)
}
end
def to_s
@cell
end
def inspect
"<#{self.class} #{@cell}>"
end
private
def validate_direction(direction)
unless NEIGHBOUR_DIRECTIONS.map(&:to_s).include?(direction.to_s)
raise "unsupported direction #{direction}"
end
end
def valid_piece?(piece)
piece != EMPTY
end
end
class Grid
attr_reader :gameover, :draw
def initialize(width)
@cells = Array.new(width * width).map { Cell.new }
@grid = @cells.each_slice(width).to_a
@gameover = false
@draw = false
@width = width
assign_cell_neighbours
end
def update?(move, piece)
x, y = move
if x.negative? || x >= @width || y.negative? || y >= @width
return false
end
cell = @grid.dig(x,y)
if not cell.place?(piece)
return false
end
if not full?
@gameover = cell.win?
else
@gameover = true
@draw = true
end
return true
end
def full?
@cells.none?{|cell| cell.empty?}
end
def to_s
@grid.map {|row| row.map(&:to_s).join}.join("n")
end
private
def assign_cell_neighbours
@grid.each_with_index do |row, row_index|
row.each_with_index do |cell, column_index|
Cell::RELATIVE_NEIGHBOUR_COORDINATES.each do |dir, rel_coord|
(rel_row_index, rel_column_index) = rel_coord
neighbour_row_index = row_index
neighbour_column_index = column_index
neighbours =
loop do
neighbour_row_index += rel_row_index
neighbour_column_index += rel_column_index
break if neighbour_row_index.negative? ||
neighbour_column_index.negative? ||
neighbour_row_index >= @width ||
neighbour_column_index >= @width
neighbours << @grid.dig(neighbour_row_index, neighbour_column_index)
end
cell[dir] = neighbours
end
end
end
end
end
class Game
def initialize(width=15)
@width = width
@users = ["A", "B"]
@user_piece = {"A"=>"+", "B"=>"*"}
@user_index = 0
end
def reset
@grid = Grid.new(@width)
end
def start
reset
puts @grid
until @grid.gameover
user = @users[@user_index]
print "Now for user<#{user}>, Enter your move(split by space)[0-#{@width-1}]:"
begin
move = gets.chomp.split.map(&:to_i)
if not @grid.update?(move, @user_piece[user])
puts "Invalid move!!!"
else
switch_user
puts @grid
end
rescue
puts "Invalid move!!!"
end
end
show_result
end
def switch_user
@user_index = (@user_index + 1) % @users.length
end
def show_result
if not @grid.draw
switch_user
print "Game Over the Winner is <#{@users[@user_index]}>"
else
print "Game Over Draw"
end
end
end
game = Game.new()
game.start
All reviews are welcome!
ruby
add a comment |
I learnt lots about ruby from my last post Game of Life in Ruby, so I have my next try in Ruby, it is Gomoku.
It is still an console game and I use three classes Game
Grid
Cell
, the structure is similar with the @Johan Wentholt 's example code. But of course game rule is different.
Game class
Game class to run the game
class Game
def initialize(width=15)
@width = width
@users = ["A", "B"]
@user_piece = {"A"=>"+", "B"=>"*"}
@user_index = 0
end
def reset
@grid = Grid.new(@width)
end
def start
reset
puts @grid
until @grid.gameover
user = @users[@user_index]
print "Now for user<#{user}>, Enter your move(split by space)[0-#{@width-1}]:"
begin
move = gets.chomp.split.map(&:to_i)
if not @grid.update?(move, @user_piece[user])
puts "Invalid move!!!"
else
switch_user
puts @grid
end
rescue
puts "Invalid move!!!"
end
end
show_result
end
def switch_user
@user_index = (@user_index + 1) % @users.length
end
def show_result
if not @grid.draw
switch_user
print "Game Over the Winner is <#{@users[@user_index]}>"
else
print "Game Over Draw"
end
end
end
Grid class
Grid present is the gomoku game board, keep updating piece on board and whether the game is over
class Grid
attr_reader :gameover, :draw
def initialize(width)
@cells = Array.new(width * width).map { Cell.new }
@grid = @cells.each_slice(width).to_a
@gameover = false
@draw = false
@width = width
assign_cell_neighbours
end
def update?(move, piece)
x, y = move
if x.negative? || x >= @width || y.negative? || y >= @width
return false
end
cell = @grid.dig(x,y)
if not cell.place?(piece)
return false
end
if not full?
@gameover = cell.win?
else
@gameover = true
@draw = true
end
return true
end
def full?
@cells.none?{|cell| cell.empty?}
end
def to_s
@grid.map {|row| row.map(&:to_s).join}.join("n")
end
private
def assign_cell_neighbours
@grid.each_with_index do |row, row_index|
row.each_with_index do |cell, column_index|
Cell::RELATIVE_NEIGHBOUR_COORDINATES.each do |dir, rel_coord|
(rel_row_index, rel_column_index) = rel_coord
neighbour_row_index = row_index
neighbour_column_index = column_index
neighbours =
loop do
neighbour_row_index += rel_row_index
neighbour_column_index += rel_column_index
break if neighbour_row_index.negative? ||
neighbour_column_index.negative? ||
neighbour_row_index >= @width ||
neighbour_column_index >= @width
neighbours << @grid.dig(neighbour_row_index, neighbour_column_index)
end
cell[dir] = neighbours
end
end
end
end
end
Cell class
Cell class keep the cell state and alse judge whether current update on cell lead to a win
class Cell
RELATIVE_NEIGHBOUR_COORDINATES = {
north: [-1, 0].freeze, north_east: [-1, 1].freeze,
east: [0, 1].freeze, south_east: [1, 1].freeze,
south: [1, 0].freeze, south_west: [1, -1].freeze,
west: [0, -1].freeze, north_west: [-1, -1].freeze,
}.freeze
NEIGHBOUR_DIRECTIONS = RELATIVE_NEIGHBOUR_COORDINATES.keys.freeze
PAIR_DIRECTIONS = [[:north, :south].freeze,
[:east, :west].freeze,
[:north_east, :south_west].freeze,
[:north_west, :south_east].freeze].freeze
EMPTY = "."
attr_accessor(*NEIGHBOUR_DIRECTIONS)
def initialize
@cell = EMPTY
end
def empty?
@cell == EMPTY
end
def place?(piece)
if empty? and valid_piece?(piece)
@cell = piece
return true
end
return false
end
def win?
neighbours.compact.select{|x| x>=4}.length > 0
end
def (direction)
validate_direction(direction)
send(direction)
end
def =(direction, neighbour)
validate_direction(direction)
send("#{direction}=", neighbour)
end
def neighbours
PAIR_DIRECTIONS.map{ |directions|
directions.map{|direction|
self[direction].find_index{|neighbour| neighbour.to_s != self.to_s}
}.compact.inject(:+)
}
end
def to_s
@cell
end
def inspect
"<#{self.class} #{@cell}>"
end
private
def validate_direction(direction)
unless NEIGHBOUR_DIRECTIONS.map(&:to_s).include?(direction.to_s)
raise "unsupported direction #{direction}"
end
end
def valid_piece?(piece)
piece != EMPTY
end
end
Full code
#!/usr/bin/ruby
class Cell
RELATIVE_NEIGHBOUR_COORDINATES = {
north: [-1, 0].freeze, north_east: [-1, 1].freeze,
east: [0, 1].freeze, south_east: [1, 1].freeze,
south: [1, 0].freeze, south_west: [1, -1].freeze,
west: [0, -1].freeze, north_west: [-1, -1].freeze,
}.freeze
NEIGHBOUR_DIRECTIONS = RELATIVE_NEIGHBOUR_COORDINATES.keys.freeze
PAIR_DIRECTIONS = [[:north, :south].freeze,
[:east, :west].freeze,
[:north_east, :south_west].freeze,
[:north_west, :south_east].freeze].freeze
EMPTY = "."
attr_accessor(*NEIGHBOUR_DIRECTIONS)
def initialize
@cell = EMPTY
end
def empty?
@cell == EMPTY
end
def place?(piece)
if empty? and valid_piece?(piece)
@cell = piece
return true
end
return false
end
def win?
neighbours.compact.select{|x| x>=4}.length > 0
end
def (direction)
validate_direction(direction)
send(direction)
end
def =(direction, neighbour)
validate_direction(direction)
send("#{direction}=", neighbour)
end
def neighbours
PAIR_DIRECTIONS.map{ |directions|
directions.map{|direction|
self[direction].find_index{|neighbour| neighbour.to_s != self.to_s}
}.compact.inject(:+)
}
end
def to_s
@cell
end
def inspect
"<#{self.class} #{@cell}>"
end
private
def validate_direction(direction)
unless NEIGHBOUR_DIRECTIONS.map(&:to_s).include?(direction.to_s)
raise "unsupported direction #{direction}"
end
end
def valid_piece?(piece)
piece != EMPTY
end
end
class Grid
attr_reader :gameover, :draw
def initialize(width)
@cells = Array.new(width * width).map { Cell.new }
@grid = @cells.each_slice(width).to_a
@gameover = false
@draw = false
@width = width
assign_cell_neighbours
end
def update?(move, piece)
x, y = move
if x.negative? || x >= @width || y.negative? || y >= @width
return false
end
cell = @grid.dig(x,y)
if not cell.place?(piece)
return false
end
if not full?
@gameover = cell.win?
else
@gameover = true
@draw = true
end
return true
end
def full?
@cells.none?{|cell| cell.empty?}
end
def to_s
@grid.map {|row| row.map(&:to_s).join}.join("n")
end
private
def assign_cell_neighbours
@grid.each_with_index do |row, row_index|
row.each_with_index do |cell, column_index|
Cell::RELATIVE_NEIGHBOUR_COORDINATES.each do |dir, rel_coord|
(rel_row_index, rel_column_index) = rel_coord
neighbour_row_index = row_index
neighbour_column_index = column_index
neighbours =
loop do
neighbour_row_index += rel_row_index
neighbour_column_index += rel_column_index
break if neighbour_row_index.negative? ||
neighbour_column_index.negative? ||
neighbour_row_index >= @width ||
neighbour_column_index >= @width
neighbours << @grid.dig(neighbour_row_index, neighbour_column_index)
end
cell[dir] = neighbours
end
end
end
end
end
class Game
def initialize(width=15)
@width = width
@users = ["A", "B"]
@user_piece = {"A"=>"+", "B"=>"*"}
@user_index = 0
end
def reset
@grid = Grid.new(@width)
end
def start
reset
puts @grid
until @grid.gameover
user = @users[@user_index]
print "Now for user<#{user}>, Enter your move(split by space)[0-#{@width-1}]:"
begin
move = gets.chomp.split.map(&:to_i)
if not @grid.update?(move, @user_piece[user])
puts "Invalid move!!!"
else
switch_user
puts @grid
end
rescue
puts "Invalid move!!!"
end
end
show_result
end
def switch_user
@user_index = (@user_index + 1) % @users.length
end
def show_result
if not @grid.draw
switch_user
print "Game Over the Winner is <#{@users[@user_index]}>"
else
print "Game Over Draw"
end
end
end
game = Game.new()
game.start
All reviews are welcome!
ruby
add a comment |
I learnt lots about ruby from my last post Game of Life in Ruby, so I have my next try in Ruby, it is Gomoku.
It is still an console game and I use three classes Game
Grid
Cell
, the structure is similar with the @Johan Wentholt 's example code. But of course game rule is different.
Game class
Game class to run the game
class Game
def initialize(width=15)
@width = width
@users = ["A", "B"]
@user_piece = {"A"=>"+", "B"=>"*"}
@user_index = 0
end
def reset
@grid = Grid.new(@width)
end
def start
reset
puts @grid
until @grid.gameover
user = @users[@user_index]
print "Now for user<#{user}>, Enter your move(split by space)[0-#{@width-1}]:"
begin
move = gets.chomp.split.map(&:to_i)
if not @grid.update?(move, @user_piece[user])
puts "Invalid move!!!"
else
switch_user
puts @grid
end
rescue
puts "Invalid move!!!"
end
end
show_result
end
def switch_user
@user_index = (@user_index + 1) % @users.length
end
def show_result
if not @grid.draw
switch_user
print "Game Over the Winner is <#{@users[@user_index]}>"
else
print "Game Over Draw"
end
end
end
Grid class
Grid present is the gomoku game board, keep updating piece on board and whether the game is over
class Grid
attr_reader :gameover, :draw
def initialize(width)
@cells = Array.new(width * width).map { Cell.new }
@grid = @cells.each_slice(width).to_a
@gameover = false
@draw = false
@width = width
assign_cell_neighbours
end
def update?(move, piece)
x, y = move
if x.negative? || x >= @width || y.negative? || y >= @width
return false
end
cell = @grid.dig(x,y)
if not cell.place?(piece)
return false
end
if not full?
@gameover = cell.win?
else
@gameover = true
@draw = true
end
return true
end
def full?
@cells.none?{|cell| cell.empty?}
end
def to_s
@grid.map {|row| row.map(&:to_s).join}.join("n")
end
private
def assign_cell_neighbours
@grid.each_with_index do |row, row_index|
row.each_with_index do |cell, column_index|
Cell::RELATIVE_NEIGHBOUR_COORDINATES.each do |dir, rel_coord|
(rel_row_index, rel_column_index) = rel_coord
neighbour_row_index = row_index
neighbour_column_index = column_index
neighbours =
loop do
neighbour_row_index += rel_row_index
neighbour_column_index += rel_column_index
break if neighbour_row_index.negative? ||
neighbour_column_index.negative? ||
neighbour_row_index >= @width ||
neighbour_column_index >= @width
neighbours << @grid.dig(neighbour_row_index, neighbour_column_index)
end
cell[dir] = neighbours
end
end
end
end
end
Cell class
Cell class keep the cell state and alse judge whether current update on cell lead to a win
class Cell
RELATIVE_NEIGHBOUR_COORDINATES = {
north: [-1, 0].freeze, north_east: [-1, 1].freeze,
east: [0, 1].freeze, south_east: [1, 1].freeze,
south: [1, 0].freeze, south_west: [1, -1].freeze,
west: [0, -1].freeze, north_west: [-1, -1].freeze,
}.freeze
NEIGHBOUR_DIRECTIONS = RELATIVE_NEIGHBOUR_COORDINATES.keys.freeze
PAIR_DIRECTIONS = [[:north, :south].freeze,
[:east, :west].freeze,
[:north_east, :south_west].freeze,
[:north_west, :south_east].freeze].freeze
EMPTY = "."
attr_accessor(*NEIGHBOUR_DIRECTIONS)
def initialize
@cell = EMPTY
end
def empty?
@cell == EMPTY
end
def place?(piece)
if empty? and valid_piece?(piece)
@cell = piece
return true
end
return false
end
def win?
neighbours.compact.select{|x| x>=4}.length > 0
end
def (direction)
validate_direction(direction)
send(direction)
end
def =(direction, neighbour)
validate_direction(direction)
send("#{direction}=", neighbour)
end
def neighbours
PAIR_DIRECTIONS.map{ |directions|
directions.map{|direction|
self[direction].find_index{|neighbour| neighbour.to_s != self.to_s}
}.compact.inject(:+)
}
end
def to_s
@cell
end
def inspect
"<#{self.class} #{@cell}>"
end
private
def validate_direction(direction)
unless NEIGHBOUR_DIRECTIONS.map(&:to_s).include?(direction.to_s)
raise "unsupported direction #{direction}"
end
end
def valid_piece?(piece)
piece != EMPTY
end
end
Full code
#!/usr/bin/ruby
class Cell
RELATIVE_NEIGHBOUR_COORDINATES = {
north: [-1, 0].freeze, north_east: [-1, 1].freeze,
east: [0, 1].freeze, south_east: [1, 1].freeze,
south: [1, 0].freeze, south_west: [1, -1].freeze,
west: [0, -1].freeze, north_west: [-1, -1].freeze,
}.freeze
NEIGHBOUR_DIRECTIONS = RELATIVE_NEIGHBOUR_COORDINATES.keys.freeze
PAIR_DIRECTIONS = [[:north, :south].freeze,
[:east, :west].freeze,
[:north_east, :south_west].freeze,
[:north_west, :south_east].freeze].freeze
EMPTY = "."
attr_accessor(*NEIGHBOUR_DIRECTIONS)
def initialize
@cell = EMPTY
end
def empty?
@cell == EMPTY
end
def place?(piece)
if empty? and valid_piece?(piece)
@cell = piece
return true
end
return false
end
def win?
neighbours.compact.select{|x| x>=4}.length > 0
end
def (direction)
validate_direction(direction)
send(direction)
end
def =(direction, neighbour)
validate_direction(direction)
send("#{direction}=", neighbour)
end
def neighbours
PAIR_DIRECTIONS.map{ |directions|
directions.map{|direction|
self[direction].find_index{|neighbour| neighbour.to_s != self.to_s}
}.compact.inject(:+)
}
end
def to_s
@cell
end
def inspect
"<#{self.class} #{@cell}>"
end
private
def validate_direction(direction)
unless NEIGHBOUR_DIRECTIONS.map(&:to_s).include?(direction.to_s)
raise "unsupported direction #{direction}"
end
end
def valid_piece?(piece)
piece != EMPTY
end
end
class Grid
attr_reader :gameover, :draw
def initialize(width)
@cells = Array.new(width * width).map { Cell.new }
@grid = @cells.each_slice(width).to_a
@gameover = false
@draw = false
@width = width
assign_cell_neighbours
end
def update?(move, piece)
x, y = move
if x.negative? || x >= @width || y.negative? || y >= @width
return false
end
cell = @grid.dig(x,y)
if not cell.place?(piece)
return false
end
if not full?
@gameover = cell.win?
else
@gameover = true
@draw = true
end
return true
end
def full?
@cells.none?{|cell| cell.empty?}
end
def to_s
@grid.map {|row| row.map(&:to_s).join}.join("n")
end
private
def assign_cell_neighbours
@grid.each_with_index do |row, row_index|
row.each_with_index do |cell, column_index|
Cell::RELATIVE_NEIGHBOUR_COORDINATES.each do |dir, rel_coord|
(rel_row_index, rel_column_index) = rel_coord
neighbour_row_index = row_index
neighbour_column_index = column_index
neighbours =
loop do
neighbour_row_index += rel_row_index
neighbour_column_index += rel_column_index
break if neighbour_row_index.negative? ||
neighbour_column_index.negative? ||
neighbour_row_index >= @width ||
neighbour_column_index >= @width
neighbours << @grid.dig(neighbour_row_index, neighbour_column_index)
end
cell[dir] = neighbours
end
end
end
end
end
class Game
def initialize(width=15)
@width = width
@users = ["A", "B"]
@user_piece = {"A"=>"+", "B"=>"*"}
@user_index = 0
end
def reset
@grid = Grid.new(@width)
end
def start
reset
puts @grid
until @grid.gameover
user = @users[@user_index]
print "Now for user<#{user}>, Enter your move(split by space)[0-#{@width-1}]:"
begin
move = gets.chomp.split.map(&:to_i)
if not @grid.update?(move, @user_piece[user])
puts "Invalid move!!!"
else
switch_user
puts @grid
end
rescue
puts "Invalid move!!!"
end
end
show_result
end
def switch_user
@user_index = (@user_index + 1) % @users.length
end
def show_result
if not @grid.draw
switch_user
print "Game Over the Winner is <#{@users[@user_index]}>"
else
print "Game Over Draw"
end
end
end
game = Game.new()
game.start
All reviews are welcome!
ruby
I learnt lots about ruby from my last post Game of Life in Ruby, so I have my next try in Ruby, it is Gomoku.
It is still an console game and I use three classes Game
Grid
Cell
, the structure is similar with the @Johan Wentholt 's example code. But of course game rule is different.
Game class
Game class to run the game
class Game
def initialize(width=15)
@width = width
@users = ["A", "B"]
@user_piece = {"A"=>"+", "B"=>"*"}
@user_index = 0
end
def reset
@grid = Grid.new(@width)
end
def start
reset
puts @grid
until @grid.gameover
user = @users[@user_index]
print "Now for user<#{user}>, Enter your move(split by space)[0-#{@width-1}]:"
begin
move = gets.chomp.split.map(&:to_i)
if not @grid.update?(move, @user_piece[user])
puts "Invalid move!!!"
else
switch_user
puts @grid
end
rescue
puts "Invalid move!!!"
end
end
show_result
end
def switch_user
@user_index = (@user_index + 1) % @users.length
end
def show_result
if not @grid.draw
switch_user
print "Game Over the Winner is <#{@users[@user_index]}>"
else
print "Game Over Draw"
end
end
end
Grid class
Grid present is the gomoku game board, keep updating piece on board and whether the game is over
class Grid
attr_reader :gameover, :draw
def initialize(width)
@cells = Array.new(width * width).map { Cell.new }
@grid = @cells.each_slice(width).to_a
@gameover = false
@draw = false
@width = width
assign_cell_neighbours
end
def update?(move, piece)
x, y = move
if x.negative? || x >= @width || y.negative? || y >= @width
return false
end
cell = @grid.dig(x,y)
if not cell.place?(piece)
return false
end
if not full?
@gameover = cell.win?
else
@gameover = true
@draw = true
end
return true
end
def full?
@cells.none?{|cell| cell.empty?}
end
def to_s
@grid.map {|row| row.map(&:to_s).join}.join("n")
end
private
def assign_cell_neighbours
@grid.each_with_index do |row, row_index|
row.each_with_index do |cell, column_index|
Cell::RELATIVE_NEIGHBOUR_COORDINATES.each do |dir, rel_coord|
(rel_row_index, rel_column_index) = rel_coord
neighbour_row_index = row_index
neighbour_column_index = column_index
neighbours =
loop do
neighbour_row_index += rel_row_index
neighbour_column_index += rel_column_index
break if neighbour_row_index.negative? ||
neighbour_column_index.negative? ||
neighbour_row_index >= @width ||
neighbour_column_index >= @width
neighbours << @grid.dig(neighbour_row_index, neighbour_column_index)
end
cell[dir] = neighbours
end
end
end
end
end
Cell class
Cell class keep the cell state and alse judge whether current update on cell lead to a win
class Cell
RELATIVE_NEIGHBOUR_COORDINATES = {
north: [-1, 0].freeze, north_east: [-1, 1].freeze,
east: [0, 1].freeze, south_east: [1, 1].freeze,
south: [1, 0].freeze, south_west: [1, -1].freeze,
west: [0, -1].freeze, north_west: [-1, -1].freeze,
}.freeze
NEIGHBOUR_DIRECTIONS = RELATIVE_NEIGHBOUR_COORDINATES.keys.freeze
PAIR_DIRECTIONS = [[:north, :south].freeze,
[:east, :west].freeze,
[:north_east, :south_west].freeze,
[:north_west, :south_east].freeze].freeze
EMPTY = "."
attr_accessor(*NEIGHBOUR_DIRECTIONS)
def initialize
@cell = EMPTY
end
def empty?
@cell == EMPTY
end
def place?(piece)
if empty? and valid_piece?(piece)
@cell = piece
return true
end
return false
end
def win?
neighbours.compact.select{|x| x>=4}.length > 0
end
def (direction)
validate_direction(direction)
send(direction)
end
def =(direction, neighbour)
validate_direction(direction)
send("#{direction}=", neighbour)
end
def neighbours
PAIR_DIRECTIONS.map{ |directions|
directions.map{|direction|
self[direction].find_index{|neighbour| neighbour.to_s != self.to_s}
}.compact.inject(:+)
}
end
def to_s
@cell
end
def inspect
"<#{self.class} #{@cell}>"
end
private
def validate_direction(direction)
unless NEIGHBOUR_DIRECTIONS.map(&:to_s).include?(direction.to_s)
raise "unsupported direction #{direction}"
end
end
def valid_piece?(piece)
piece != EMPTY
end
end
Full code
#!/usr/bin/ruby
class Cell
RELATIVE_NEIGHBOUR_COORDINATES = {
north: [-1, 0].freeze, north_east: [-1, 1].freeze,
east: [0, 1].freeze, south_east: [1, 1].freeze,
south: [1, 0].freeze, south_west: [1, -1].freeze,
west: [0, -1].freeze, north_west: [-1, -1].freeze,
}.freeze
NEIGHBOUR_DIRECTIONS = RELATIVE_NEIGHBOUR_COORDINATES.keys.freeze
PAIR_DIRECTIONS = [[:north, :south].freeze,
[:east, :west].freeze,
[:north_east, :south_west].freeze,
[:north_west, :south_east].freeze].freeze
EMPTY = "."
attr_accessor(*NEIGHBOUR_DIRECTIONS)
def initialize
@cell = EMPTY
end
def empty?
@cell == EMPTY
end
def place?(piece)
if empty? and valid_piece?(piece)
@cell = piece
return true
end
return false
end
def win?
neighbours.compact.select{|x| x>=4}.length > 0
end
def (direction)
validate_direction(direction)
send(direction)
end
def =(direction, neighbour)
validate_direction(direction)
send("#{direction}=", neighbour)
end
def neighbours
PAIR_DIRECTIONS.map{ |directions|
directions.map{|direction|
self[direction].find_index{|neighbour| neighbour.to_s != self.to_s}
}.compact.inject(:+)
}
end
def to_s
@cell
end
def inspect
"<#{self.class} #{@cell}>"
end
private
def validate_direction(direction)
unless NEIGHBOUR_DIRECTIONS.map(&:to_s).include?(direction.to_s)
raise "unsupported direction #{direction}"
end
end
def valid_piece?(piece)
piece != EMPTY
end
end
class Grid
attr_reader :gameover, :draw
def initialize(width)
@cells = Array.new(width * width).map { Cell.new }
@grid = @cells.each_slice(width).to_a
@gameover = false
@draw = false
@width = width
assign_cell_neighbours
end
def update?(move, piece)
x, y = move
if x.negative? || x >= @width || y.negative? || y >= @width
return false
end
cell = @grid.dig(x,y)
if not cell.place?(piece)
return false
end
if not full?
@gameover = cell.win?
else
@gameover = true
@draw = true
end
return true
end
def full?
@cells.none?{|cell| cell.empty?}
end
def to_s
@grid.map {|row| row.map(&:to_s).join}.join("n")
end
private
def assign_cell_neighbours
@grid.each_with_index do |row, row_index|
row.each_with_index do |cell, column_index|
Cell::RELATIVE_NEIGHBOUR_COORDINATES.each do |dir, rel_coord|
(rel_row_index, rel_column_index) = rel_coord
neighbour_row_index = row_index
neighbour_column_index = column_index
neighbours =
loop do
neighbour_row_index += rel_row_index
neighbour_column_index += rel_column_index
break if neighbour_row_index.negative? ||
neighbour_column_index.negative? ||
neighbour_row_index >= @width ||
neighbour_column_index >= @width
neighbours << @grid.dig(neighbour_row_index, neighbour_column_index)
end
cell[dir] = neighbours
end
end
end
end
end
class Game
def initialize(width=15)
@width = width
@users = ["A", "B"]
@user_piece = {"A"=>"+", "B"=>"*"}
@user_index = 0
end
def reset
@grid = Grid.new(@width)
end
def start
reset
puts @grid
until @grid.gameover
user = @users[@user_index]
print "Now for user<#{user}>, Enter your move(split by space)[0-#{@width-1}]:"
begin
move = gets.chomp.split.map(&:to_i)
if not @grid.update?(move, @user_piece[user])
puts "Invalid move!!!"
else
switch_user
puts @grid
end
rescue
puts "Invalid move!!!"
end
end
show_result
end
def switch_user
@user_index = (@user_index + 1) % @users.length
end
def show_result
if not @grid.draw
switch_user
print "Game Over the Winner is <#{@users[@user_index]}>"
else
print "Game Over Draw"
end
end
end
game = Game.new()
game.start
All reviews are welcome!
ruby
ruby
asked 2 days ago
Aries_is_there
687211
687211
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210788%2fgomoku-game-in-ruby%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210788%2fgomoku-game-in-ruby%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown