Last active
August 29, 2015 14:19
-
-
Save jtompkins/57f06a536476ac7a9ae3 to your computer and use it in GitHub Desktop.
RubyQuiz #117 - SimFrost
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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