Skip to content

Instantly share code, notes, and snippets.

@jtompkins
Last active August 29, 2015 14:19
Show Gist options
  • Save jtompkins/57f06a536476ac7a9ae3 to your computer and use it in GitHub Desktop.
Save jtompkins/57f06a536476ac7a9ae3 to your computer and use it in GitHub Desktop.
RubyQuiz #117 - SimFrost
# A weird, mostly ugly, kind of functional-programming-ish solution
# to RubyQuiz #117:
# http://rubyquiz.com/quiz117.html
STATES = {
vacuum: " ",
vapor: ".",
frost: "*"
}
class Grid
def self.empty(h, w)
Grid.new h, w
end
def self.populated(h, w, vapor)
grid = Grid.new h, w
(0...h).each do |y|
(0...w).each do |x|
if rand(100) < vapor
grid.set([x, y], STATES[:vapor])
end
end
end
grid.set([w/2, h/2], STATES[:frost])
grid
end
attr_reader :w, :h, :cells
def wrap_coords(x, y)
[
x >= @w ? 0 : x,
y >= @h ? 0 : y
]
end
def get(coords)
x = coords.first
y = coords.last
@cells[y][x]
end
def set(coords, value)
x = coords.first
y = coords.last
@cells[y][x] = value
end
def done?
@cells.flatten.none? { |c| c == STATES[:vapor] }
end
def to_s
print " "
(1..@w).each { |col| print " #{col}" }
puts
print_divider
@cells.each_with_index do |row, i|
print "#{i+1} |"
print row.join("|")
puts "|"
print_divider
end
end
private
def initialize(h, w)
@w = w
@h = h
@cells = Array.new(h) do
Array.new(w) { STATES[:vacuum] }
end
end
def print_divider
print " "
@w.times { print "+-" }
puts "+"
end
end
class Neighborhood
attr_reader :coords, :values
def initialize(coords, values)
@coords = coords
@values = values
end
def has_frost?
values.any? { |v| v == STATES[:frost] }
end
def freeze
vals = @values.dup
new_vals = vals.map { |v| v == STATES[:vapor] ? STATES[:frost] : v }
Neighborhood.new(@coords.dup, new_vals)
end
def rotate
vals = @values.dup
if rand(2) == 0
vals = vals.unshift(vals.pop)
else
vals = vals.push(vals.shift)
end
Neighborhood.new(@coords.dup, vals)
end
def transform
if has_frost?
freeze
else
rotate
end
end
end
def by_neighborhood(grid, offset)
Enumerator.new do |yielder|
(offset...grid.h).step(2).each do |y|
(offset...grid.w).step(2).each do |x|
coords = [
grid.wrap_coords(x, y),
grid.wrap_coords(x + 1, y),
grid.wrap_coords(x + 1, y + 1),
grid.wrap_coords(x, y + 1)
]
values = coords.map { |c| grid.get(c) }
yielder << Neighborhood.new(coords, values)
end
end
end
end
def simulate
grid = Grid::populated(10, 10, 30)
puts
puts grid
puts
offset = 0
loop do
grid = by_neighborhood(grid, offset)
.map(&:transform)
.reduce(Grid::empty(10, 10)) do |g, n|
n.coords.zip(n.values) { |coord, val| g.set(coord, val) }
g
end
offset = offset == 0 ? 1 : 0
break if grid.done?
end
puts
puts grid
puts
end
simulate
class Board
@@states = {
vacuum: " ",
vapor: ".",
frost: "*"
}
attr_reader :w, :h
def initialize(w, h, vapor)
@w = w
@h = h
# fill an arra with vapor and vacuum
@board = Array.new(h) do
Array.new(w) do
if rand(100) < vapor
@@states[:vapor]
else
@@states[:vacuum]
end
end
end
# set the center square to be frost
@board[@h/2][@w/2] = @@states[:frost]
end
def by_neighborhood(offset = 0, &block)
(offset...@h).step(2).each do |y|
(offset...@w).step(2).each do |x|
yield create_neighborhood(x, y)
end
end
end
def to_s
print " "
(1..@w).each { |col| print " #{col}" }
puts
print_divider
@board.each_with_index do |row, i|
print "#{i+1} |"
print row.join("|")
puts "|"
print_divider
end
end
def get(coord)
@board[coord[:y]][coord[:x]]
end
def set(coord, value)
@board[coord[:y]][coord[:x]] = value
end
def tick(offset)
by_neighborhood(offset) do |n|
if has_frost?(n)
freeze(n)
else
rotate(n)
end
end
end
def done?
@board.flatten.none? { |c| c == @@states[:vapor] }
end
private
def print_divider
print " "
@w.times { print "+-" }
puts "+"
end
def wrap_x(x)
x1 = x + 1
if x1 >= @w
0
else
x1
end
end
def wrap_y(y)
y1 = y + 1
if y1 >= @h
0
else
y1
end
end
def create_neighborhood(x, y)
x1 = wrap_x x
y1 = wrap_y y
[
{x: x, y: y},
{x: x, y: y1},
{x: x1, y: y1},
{x: x1, y: y},
]
end
def has_frost?(n)
n.any? { |c| get(c) == @@states[:frost] }
end
def freeze(n)
n.each { |c| set(c, @@states[:frost]) if get(c) == @@states[:vapor] }
end
def rotate(n)
new_n = n.dup
if rand(2) == 0
new_n = new_n.unshift(new_n.pop)
else
new_n = new_n.push(new_n.shift)
end
vals = new_n.map { |c| get(c) }
n.zip(vals) { |c, val| set(c, val) }
end
end
def simulate
board = Board.new 10, 10, 25
puts board
offset = 0
loop do
board.tick(offset)
offset = offset == 1 ? 0 : 1
break if board.done?
end
puts
puts board
end
simulate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment