Skip to content

Instantly share code, notes, and snippets.

@joker1007
Created October 12, 2014 13:56
Show Gist options
  • Save joker1007/fa5407ecef312d94cb30 to your computer and use it in GitHub Desktop.
Save joker1007/fa5407ecef312d94cb30 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'pp'
require 'rspec-power_assert'
class Point
attr_reader :row, :col
def initialize(row, col)
@row, @col = row, col
end
def ==(other)
other.is_a?(Point) && row == other.row && col == other.col
end
def is_out_of_table?(table)
!(table.row_range.cover?(row) && table.col_range.cover?(col))
end
def around_points_with_direction(direction)
start_point = Point.new(row - 1, col - 1)
direction.new(start_point).map(&:itself)
end
def right_point
Point.new(row, col + 1)
end
def left_point
Point.new(row, col - 1)
end
def up_point
Point.new(row - 1, col)
end
def down_point
Point.new(row + 1, col)
end
end
class Table
attr_reader :data, :min_row, :min_col, :max_row, :max_col, :last_rotate_chars
def initialize(size = 5)
@min_row, @min_col = 0, 0
@max_row, @max_col = size - 1, size - 1
initial_char = 'a'
@data = size.times.each_with_object([]) do |_, table|
table << []
size.times do
table.last << initial_char
initial_char = initial_char.succ
end
end
end
def row_range
@min_row..@max_row
end
def col_range
@min_col..@max_col
end
def get_point_of(char)
@data.each_with_index do |row, row_count|
row.each_with_index do |col, col_count|
if col == char
return Point.new(row_count, col_count)
end
end
end
nil
end
def result
@last_rotate_chars.sort.join
end
def rotate(cmds)
cmds.each do |cmd|
do_rotate(cmd)
end
self
end
def do_rotate(cmd)
@last_rotate_chars = []
point = get_point_of(cmd.char)
around_points = point.around_points_with_direction(cmd.direction).reject do |pt|
pt.is_out_of_table?(self)
end
around_points.inject(@data[around_points.last.row][around_points.last.col]) do |pre, pt|
store = @data[pt.row][pt.col].dup
@last_rotate_chars << store
@data[pt.row][pt.col] = pre
store
end
end
end
module DirectionStep
module Up
def self.next_point_from(point)
point.up_point
end
end
module Down
def self.next_point_from(point)
point.down_point
end
end
module Right
def self.next_point_from(point)
point.right_point
end
end
module Left
def self.next_point_from(point)
point.left_point
end
end
end
class DirectionTraverser
include Enumerable
def initialize(start_point)
@start_point = start_point
@point = nil
end
def each
if block_given?
while set_next_point
yield @point
end
else
Enumerator.new do |y|
while set_next_point
y << @point
end
end
end
end
def set_next_point
unless @point
@point = @start_point
return @point
end
@point = @direction.next_point_from(@point)
set_direction
@point == @start_point ? nil : @point
end
end
class ClockwiseTraverser < DirectionTraverser
def initialize(start_point)
super
@direction = DirectionStep::Right
end
def set_direction
if @point.col == @start_point.col + 2 && @point.row < @start_point.row + 2
@direction = DirectionStep::Down
elsif @point.row == @start_point.row + 2 && @point.col > @start_point.col
@direction = DirectionStep::Left
elsif @point.col == @start_point.col
@direction = DirectionStep::Up
end
end
end
class CounterClockwiseTraverser < DirectionTraverser
def initialize(start_point)
super
@direction = DirectionStep::Down
end
def set_direction
if @point.row == @start_point.row + 2 && @point.col < @start_point.col + 2
@direction = DirectionStep::Right
elsif @point.col == @start_point.col + 2 && @point.row > @start_point.row
@direction = DirectionStep::Up
elsif @point.row == @start_point.row
@direction = DirectionStep::Left
end
end
end
class Command
attr_reader :direction, :char
def initialize(direction, char)
@direction, @char = direction, char.downcase
end
def self.parse(str)
str.each_char.map do |c|
if c == c.upcase
Command.new(CounterClockwiseTraverser, c)
else
Command.new(ClockwiseTraverser, c)
end
end
end
end
TEST_DATA = [
[ "YokoHamarb", "acdfp" ],
[ "Ruby", "twx" ],
[ "ruby", "nst" ],
[ "PHP", "gkluv" ],
[ "a", "bfg" ],
[ "b", "acfgh" ],
[ "m", "ghilnqrs" ],
[ "mg", "bcdhilmq" ],
[ "Mg", "fhiklmpq" ],
[ "MS", "ijmnoqrt" ],
[ "mG", "bcdhilmq" ],
[ "lf", "abcghklp" ],
[ "paq", "lprvw" ],
[ "bFH", "abcfg" ],
[ "Agh", "abcfgklm" ],
[ "Msul", "pruvw" ],
[ "RSRX", "jorty" ],
[ "xTsn", "ijmos" ],
[ "FHwnQ", "lmorsvwx" ],
[ "jIDIb", "cfghilmo" ],
[ "rLGPq", "ilmnprvx" ],
[ "WRiFhd", "ceg" ],
[ "gkfLfh", "cfglmpqr" ],
[ "STydYQ", "hklmpruv" ],
[ "spaeAWJ", "cdehimns" ],
[ "xTxTXwY", "mwx" ],
[ "xyQMkMi", "cdejlnow" ],
[ "HJkxpWxA", "cdhkp" ],
[ "hgaGDodg", "djo" ],
[ "abKBmkBc", "bdfik" ],
[ "mCvlhnilm", "adfgikqr" ],
[ "StyxTYsIh", "imnostxy" ],
[ "HLHnhLMLC", "ahlmn" ],
[ "DuHmbFQysI", "ehj" ],
[ "cGfGCaLgCq", "acguv" ],
[ "OisYOrOXwq", "ilnstvwx" ],
[ "wMnYIukHAvO", "bdefhjmn" ],
[ "HCfBhKHkhDF", "abdghikn" ],
[ "pUPmwrHsYSH", "morsy" ],
[ "PnCYYWnPoUxq", "bfopv" ],
[ "DiojiYXowowr", "cilmnstw" ],
[ "HLNsiNMnbAnn", "abdkm" ],
]
TEST_DATA.each do |td|
describe "めぐるセル #{td[0]}" do
let(:table) { Table.new }
let(:cmds) { Command.parse(td[0]) }
before do
table.rotate(cmds)
end
it_is_asserted_by { table.result == td[1] }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment