横浜へなちょこプログラミング勉強会にて過去に出題されためぐるセルを解いてみた。
回答にかかった時間はぎりぎり60分。
- 指定セルの外周を取得
- 外周を時計回りさせやすいようにソート
-
Array.rotate
で時計回りとなるようにセルの入れ替え - 入れ替えたセルの内容を返す
もうちょっとうまいやり方があったんじゃないだろうかと思うが、考えているばかりで時間が過ぎてしまったのでひとまず真っ先に思いついた方法で実装してしまった。
class RotationBox
def initialize
chars = (?a..?y).to_a
@sort_priority = [:top_left, :top, :top_right, :right, :bottom_right, :bottom, :bottom_left, :left]
@box = 5.times.map{|y|
5.times.map{|x|
chars[(y * 5) + x]
}
}
end
def rotate_all input
last = input.scan(/(\w)(\w)/).inject([]){|r, (a, b)|
rotate a, b
}
last.empty? ? "none" : last.sort.join
end
private
def axis val
@box.each.with_index.inject([nil, nil]){|r, (line, y)|
if x = line.find_index(val)
break [x, y]
else
r
end
}
end
def rotate a, b
ax, ay = axis a
bx, by = axis b
top, bottom, left, right = [
ay < by ? ay : by,
ay > by ? ay : by,
ax < bx ? ax : bx,
ax > bx ? ax : bx
]
round = round(top, bottom, left, right).sort{|(axx, ayy), (bxx, byy)|
position_a = position axx, ayy, top, bottom, left, right
position_b = position bxx, byy, top, bottom, left, right
if position_a == :top and position_b == :top
axx <=> bxx
elsif position_a == :right and position_b == :right
ayy <=> byy
elsif position_a == :bottom and position_b == :bottom
(axx <=> bxx) * -1
elsif position_a == :left and position_b == :left
(ayy <=> byy) * -1
else
@sort_priority.index(position_a) <=> @sort_priority.index(position_b)
end
}
round.map{|(x, y)| @box[y][x]}.rotate(-1).each.with_index{|c, i|
x, y = round[i]
@box[y][x] = c
}
end
def block top, bottom, left, right
5.times.select{|x|
x.between? left, right
}.product(5.times.select{|y|
y.between? top, bottom
})
end
def round top, bottom, left, right
block = block top, bottom, left, right
block.map{|(x, y)|
[
[x - 1, y - 1], [x , y - 1], [x + 1, y - 1],
[x - 1, y ], [x + 1, y ],
[x - 1, y + 1], [x , y + 1], [x + 1, y + 1]
].select{|xx, yy|
xx.between?(0, 4) && yy.between?(0, 4) && !block.include?([xx, yy])
}
}.flatten(1).uniq
end
def position x, y, top, bottom, left, right
if top > y
left > x ? :top_left : (right < x ? :top_right : :top)
elsif bottom < y
left > x ? :bottom_left : (right < x ? :bottom_right : :bottom)
elsif left > x
:left
elsif right < x
:right
end
end
end
test = <<_TEST
/*0*/ test( "ab,gg,uj,pt,an,ir,rr", "hpqsvwxy" );
/*1*/ test( "gs,ok", "abcdftvwxy" );
/*2*/ test( "gs,sg,ok", "none" );
/*3*/ test( "aa,bb,hh,nn", "hiostwxy" );
/*4*/ test( "ae,ko,uy,cw", "bdgilnqsvx" );
/*5*/ test( "am,gs,am,gs,am,gs,am,gs", "cfhkmqrvwx" );
/*6*/ test( "ay", "none" );
/*7*/ test( "gs,ay", "defjkoptuv" );
/*8*/ test( "bx,ay", "none" );
/*9*/ test( "ft,ay", "defjkoptuv" );
/*10*/ test( "ab,cd,ef,gh,ij,kl,mn,op,qr,st,uv,wx", "cdjmnry" );
/*11*/ test( "wx,uv,st,qr,op,mn,kl,ij,gh,ef,cd,ab", "kmoxy" );
/*12*/ test( "am,cj,ac,em,ss,cy,aa,ee,ff,vp", "none" );
/*13*/ test( "uf,oq,gn,ss,ca,hv,ej", "none" );
/*14*/ test( "cc,wk,uu,ws,bk,aa,vv", "bei" );
/*15*/ test( "tr,ou,ll,pp,jh,vf,yy,nr,rr,oo", "rxy" );
/*16*/ test( "ky,ov,ri,qm,nn,ee,ws,em,ca,ak", "biju" );
/*17*/ test( "ty", "nosx" );
/*18*/ test( "ll,uh,hq,ss,nx,ry,ku,ab,jj", "efouv" );
/*19*/ test( "yl,mu,qj,ss,ep", "mnqru" );
/*20*/ test( "kj,ee,qk", "fglruv" );
/*21*/ test( "xi,wd,hf", "ciknqr" );
/*22*/ test( "fx,ak,cc,ce", "bdhijnp" );
/*23*/ test( "li,jf,pp,qm,hg,sf", "akntuwx" );
/*24*/ test( "jw", "bcdeglqv" );
/*25*/ test( "uk,oe,xr", "dglmoqsy" );
/*26*/ test( "bb,ov,pd,dd,xk,is,hh,xd,xx,kq,pp,ku", "cfhjopqvy" );
/*27*/ test( "iq,fn,il,ww,ox,la,or,ga,wg,ef,us", "cfgjopvxy" );
/*28*/ test( "km,po", "abcdenqrst" );
/*29*/ test( "tc,mh,cw", "abefjkoptu" );
/*30*/ test( "fm,jx,xx,pi,gs,au,uq,ut,ap,vb", "cdghjmortux" );
/*31*/ test( "ik,xl,si", "abcdflorvwx" );
/*32*/ test( "nu,cc,lv,bu,tt,ww,xk,ia,in,sa,my", "abcefgpqrstu" );
/*33*/ test( "tt,ak,xh,tk,oo,yr,na,yv,gm,vh", "degiklmnquwx" );
/*34*/ test( "kk,ob,kk,fm,xk", "acdegjlopqruy" );
/*35*/ test( "uq,ko,pf,yy,ig,tu,ve,ve,qy,mh,oo,dv", "befjkoqrtuwxy" );
/*36*/ test( "aj,hb,ar,ii,np,ki,hg,vd", "cefhjlmopqtwxy" );
/*37*/ test( "vv,sf,ww,my,mm,sq,fb,ly,fu,ls", "bfghkmnptuvwxy" );
/*38*/ test( "jj,bp,gs", "abdefijkprtuvwxy" );
/*39*/ test( "sv,sn,mn,gn,gi", "abcdefhjnpqtuvxy" );
_TEST
require 'minitest/autorun'
describe 'Rotate' do
test.split("\n").each do |line|
t, n, input, expect = line.match(/^\/\*(\d+)\*\/\s*test\(\s*"([^"]+)",\s*"([^"]+)"\s*\);*\s*$/).to_a
box = RotationBox.new
it input do
assert_equal expect, box.rotate_all(input)
end
end
end