U E D R , A S I H C RSS

Spiral Array/Leonardong

ν”ΌλΌλ―Έλ“œ 였λ΄κΈ°

2h 20m 28s
μ•„μ΄λ””μ–΄λŠ” JuNe μ„ λ°°κ°€ λ§ν–ˆλ˜ 것이닀.(μ €λ²ˆ μžλ°” μ»¨νΌλŸ°μŠ€μ—μ„œ μ˜€λ˜κ°€..) ν•œ 번 λ‚˜μ„ ν˜•μœΌλ‘œ μ§„ν–‰λ˜λŠ” 것을 같은 측으둜 λ³Έλ‹€. 그러면 κ°€μž₯ λ°”κΉ₯μͺ½μ€ 1μΈ΅, λ‹€μŒ μ•ˆμͺ½μ€ 2μΈ΅ 이런 μ‹μœΌλ‘œ μ•ˆμͺ½μœΌλ‘œ 갈수둝 높이가 λ†’μ•„μ§„λ‹€. ν•œ μ‚¬λžŒμ΄ ν”ΌλΌλ―Έλ“œλ₯Ό ν•œ 바퀴 돌고 λ‹€μŒ 측으둜 μ˜¬λΌκ°€λ©΄μ„œ μžμ‹ μ΄ λ“€λ €λ˜ 곳이 λͺ‡ λ²ˆμ§ΈμΈμ§€, μ’Œν‘œλŠ” 무엇인지 κΈ°μ–΅ν•œλ‹€. ν•œ 측을 λ‹€ 돌면 μ‹œμž‘ν–ˆλ˜ 자리둜 λŒμ•„μ˜€κΈ° λ•Œλ¬Έμ— μ€‘λ³΅ν•΄μ„œ κΈ°μ–΅ν•œ μ’Œν‘œλŠ” μ§€μš°κ³  λ‹€μŒ 측으둜 μ΄λ™ν•œλ‹€.

ν•˜μ§€λ§Œ 여지껏 κ·ΈλŸ¬ν•œ 접근법을 μ•Œκ³ μ„œλ„ TDD둜 ν’€μ§€λ₯Ό λͺ»ν–ˆμ—ˆλ‹€. 맀번 λ‚˜μ„ ν˜• "ν–‰λ ¬"에 μ–΄λ–»κ²Œ 숫자λ₯Ό μƒˆκΈΈμ§€λ§Œ μƒκ°ν–ˆκΈ° λ•Œλ¬Έμ΄λ‹€. κ·ΈλŸ¬λ‹€ λ³΄λ‹ˆ 2차원 λ°°μ—΄μ˜ 인덱슀λ₯Ό μ‘°μž‘ν•˜λŠ” 수μ€μ—μ„œ 생각이 λ²—μ–΄λ‚˜μ§ˆ λͺ»ν–ˆλ‹€. ν•˜μ§€λ§Œ 사싀은 μ›€μ§μž„(μ΄μ „μ˜ 인덱슀 μ‘°μž‘), 움직인 점듀, 행렬을 λ”°λ‘œ 생각할 수 μžˆμ—ˆλ‹€. μ•„! μ΄λ ‡κ²Œ ν…ŒμŠ€νŠΈ ν•˜λ©΄ λ˜κ² κ΅¬λ‚˜!

TDD둜 ν’€μ—ˆλ‹€λŠ” 점이 κΈ°μ˜λ‹€. μ²˜μŒλΆ€ν„° λ„ˆλ¬΄ λ©”μ„œλ“œλ₯Ό 어디에 μ†ν•˜κ²Œ ν•  μ§€ κ³ λ―Όν•˜μ§€ μ•Šκ³  μ‹œμž‘ν•œ 것이 μœ μš©ν–ˆλ‹€. κ·Έ 결과둜 μ˜ˆμ „ κ°™μœΌλ©΄ μƒκ°ν•˜μ§€ λͺ»ν–ˆμ„ Directionν΄λž˜μŠ€μ™€ κ·Έ ν•˜μœ„ ν΄λž˜μŠ€κ°€ νƒ„μƒν–ˆλ‹€. λ˜ν•œ 행렬은 μ΅œμ’… 결과물을 μ €μž₯ν•˜κ³  λ³΄μ—¬μ£ΌλŠ” μΌμ’…μ˜ 뷰처럼 μ“°μ˜€λ‹€.

ν˜„μž¬λŠ” ν–‰λ ¬ ꡬ성이 λΉ„νš¨μœ¨μ μ΄λ‹€. μ›€μ§μ˜€λ˜ 기둝을 κ°€μ§€κ³  행렬을 κ΅¬μ„±ν•˜κΈ°λ₯Ό λ°˜λ³΅ν•œλ‹€.이것을 μˆ˜μ •ν•  λ•Œ μ€λ” 효율적으둜 μž‘λ™ν•˜κ²Œ λ§Œλ“€μ–΄μ•Όκ² λ‹€. Mover클래슀, Array클래슀의 μ’…λ£Œκ²€μ‚¬, ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λŠ” ν™•μ‹€νžˆ Refactoring이 ν•„μš”ν•˜λ‹€.


~cpp
import unittest

ROW = 0
COL = 1

class Direction:
    def move(self, coordinate, board):
        if ( board.isWall( self.next(coordinate) ) ):    
            return coordinate
        return self.next(coordinate)

class Down(Direction):
    def next(self, coordinate):
        return (coordinate[ROW] + 1, coordinate[COL])
    
class Up( Direction ):
    def next(self, coordinate):
        return (coordinate[ROW] - 1, coordinate[COL])

class Right(Direction):
    def next(self, coordinate):
        return (coordinate[ROW], coordinate[COL] + 1 )

class Left(Direction):
    def next(self, coordinate):
        return (coordinate[ROW], coordinate[COL] - 1 )


class Board:
    def __init__(self):
        self.size = 0
    def setBoundary( self, start, end ):
        self.start = start
        self.end = end
    def isWall( self, coordinate ):
        if ( coordinate[ROW] < self.start ):
            return True
        elif ( coordinate[ROW] >= self.end ):
            return True
        elif ( coordinate[COL] < self.start ):
            return True
        elif ( coordinate[COL] >= self.end ):
            return True
        return False
    def decreaseBoundary(self, amount):
        self.setBoundary( self.start+amount, self.end-amount )
    

class Mover:
    def __init__(self):
        self.coordinate = (0,0)
        self.moveCount = 0
        self.history = []
    def position(self):
        return self.coordinate
    def goStraight(self, direction, board):
        while ( not self.coordinate == direction.move(self.coordinate, board) ):
            self.history.append( Point( self.coordinate, self.moveCount+1 ) )
            self.moveCount += 1
            self.coordinate = direction.move( self.coordinate, board )
        self.history.append( Point( self.coordinate, self.moveCount+1 ) )
    def mark(self, point, value):
        point.value = value
    def _setMoveCount(self, count):
        self.moveCount = count
    def _setPosition(self, coordinate):
        self.coordinate = coordinate
    def getHistory(self):
        return self.history
    def turnRound(self, board):
        self.goStraight( Right(), board )
        self.goStraight( Down(), board )
        self.goStraight( Left(), board )
        self.goStraight( Up(), board )
        board.decreaseBoundary(1)
        self.eraseLastMovement()
        self._setPosition( self.nextFloor() )
    def nextFloor(self):
        return (self.coordinate[ROW]+1, self.coordinate[COL]+1 )
    def eraseLastMovement(self):
        self._setMoveCount( self.history.pop().value - 1 )
        
class Point:
    def __init__(self, coordinate, value):
        self.coordinate = coordinate
        self.value = value

class Array:
    def __init__(self, size):
        self.matrix = []
        for i in range(size):
            self.matrix.append( [Point((-1,-1),-1)] * size )
    def isAllFilled(self):
        for row in self.matrix:
            for point in row:
                if ( point.value < 0 ):
                    return False
        return True
    def _fillAll(self):
        for row in self.matrix:
            for point in row:
                point.value = 1
    def get(self, row, col):
        return self.matrix[row][col]
    def construct(self, pointList):
        for p in pointList:
            self.matrix[p.coordinate[ROW]][p.coordinate[COL]] = p
    def printValues(self):
        for row in self.matrix:
            for point in row:
                print point.value,
                print "\t",
            print

class SpiralArrayTest(unittest.TestCase):
    def setUp(self):
        self.board = Board()
        self.size = 10
        self.board.setBoundary(start=0, end=self.size)
        self.mover = Mover()
        self.array = Array( self.size )
    def testMoveDown(self):
        self.assertEquals( (1,0), Down().move( (0,0), self.board ) )
        self.assertEquals( (self.size-1,0), Down().move( (self.size-1,0), self.board ) )
    def testMoveUp(self):
        self.assertEquals( (0,0), Up().move( (0,0), self.board ) )
        self.assertEquals( (self.size-1,0), Up().move( (self.size,0), self.board ) )
    def testIsWall(self):
        self.assertEquals( False, self.board.isWall( (0,0) ) )
        self.assertEquals( True, self.board.isWall( (-1,0) ) )
        self.assertEquals( True, self.board.isWall( (0,-1) ) )
        self.assertEquals( True, self.board.isWall( (0,self.size) ) )
        self.assertEquals( True, self.board.isWall( (self.size,0) ) )
    def testGoStraightRight(self):
        self.mover.goStraight(Right(), self.board)
        self.assertEquals( (0,self.size-1), self.mover.position() )        
    def testMark(self):
        point = Point((0,0),0)
        self.mover._setMoveCount(1)
        self.mover.mark( point, self.mover.moveCount )
        self.assertEquals( 1, point.value )
    def testStorePointCoordinate(self):
        self.mover.goStraight( Down(), self.board )
        points = self.mover.getHistory()
        self.assertEquals( (self.size-1,0), points.pop().coordinate )
        self.assertEquals( (self.size-2,0), points.pop().coordinate )
    def testStoreMoveCount(self):
        self.mover._setPosition((self.size-1, self.size-1))
        self.mover.goStraight( Left(), self.board )
        points = self.mover.getHistory()
        self.assertEquals( self.size, points.pop().value )
        self.assertEquals( self.size-1, points.pop().value )
    def testTurnRound(self):
        self.mover._setPosition( (0,0) )
        self.mover.turnRound(self.board)
        self.assertEquals( (1,0), self.mover.getHistory().pop().coordinate )
    def testIsFinished(self):
        self.assertEquals( False, self.array.isAllFilled() )
        self.array._fillAll()
        self.assertEquals( True, self.array.isAllFilled() )        
    def testConstructArray(self):
        self.mover._setPosition( (0,0) )
        self.mover.turnRound(self.board)
        self.array.construct( self.mover.getHistory() )
        self.assertEquals( (0,0), self.array.get(0,0).coordinate )
        self.assertEquals( (self.size-1, self.size-1),
                           self.array.get(self.size-1, self.size-1).coordinate )
if __name__ == "__main__":
##    unittest.main()
    inputSize = input()
    board = Board()
    board.setBoundary(0, inputSize )
    array = Array(inputSize)
    mover = Mover()
    mover._setPosition((0,0))
    while not ( array.isAllFilled() ):
        mover.turnRound(board)
        array.construct(mover.getHistory())
    array.printValues()

ν”ΌλΌλ―Έλ“œ 였λ΄κΈ° Refacotring

1h 58m 26s

μ§€λ‚œ 번 λ¦¬νŒ©ν† λ§ λŒ€μƒμ΄μ—ˆλ˜ Mover클래슀, Array클래슀의 μ’…λ£Œκ²€μ‚¬, ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λ₯Ό λ¦¬νŒ©ν† λ§ ν–ˆλ‹€. ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ™€ Arrayν΄λž˜μŠ€λŠ” μ‰½κ²Œ λ¦¬νŒ©ν† λ§ ν•  수 μžˆμ—ˆλ‹€. ν•˜μ§€λ§Œ Mover클래슀λ₯Ό μ†λŒ€λŠ”λ° 였래 κ±Έλ Έλ‹€.

goStraight μ „ 버전은 Direction클래슀λ₯Ό μ΄μš©ν•΄μ„œ μ΄λ™ν•œ(벽을 λ§Œλ‚˜λ©΄ μ΄λ™ν•˜μ§€ μ•ŠμŒ) μœ„μΉ˜λ₯Ό μ–»μ–΄λ‚΄κ³ , μ΄λ™ν•œ 기둝을 μ €μž₯ν•˜λŠ” ν˜•μ‹μ΄μ—ˆλ‹€. 벽에 λŒ€ν•œ 검사가 κ²ΉμΉ˜λŠ” 것 κ°™μ•„ moverμ—μ„œ λ°”λ‘œ 벽을 κ²€μ‚¬ν•˜κ³  벽에 λ“€μ–΄μ„œλ©΄ μ’…λ£Œν•˜λŠ” κ²ƒμœΌλ‘œ λ§Œλ“€μ—ˆλ‹€. 그러고 λ³΄λ‹ˆ λ”°λ‘œ 카운트 ν•  ν•„μš”κ°€ μ—†μ–΄ moveCountλ³€μˆ˜λ₯Ό μ—†μ•΄λ‹€. moverκ°€ μ’…λ£Œ 쑰건도 κ²€μ‚¬ν•˜λŠ”λ° board λ„“μ΄λ§ŒνΌ μ΄λ™ν–ˆμœΌλ©΄ λλ‚˜λŠ” 것이기 λ•Œλ¬Έμ΄λ‹€.

그런데 벽에 λ“€μ–΄μ„œμ•Ό μ’…λ£Œν•˜λ‹€ λ³΄λ‹ˆκΉŒ moverλ₯Ό 벽에 λ“€μ–΄κ°€κΈ° 전에 μœ„μΉ˜λ‘œ λ˜λŒλ €λ†“μ•„μ•Ό ν–ˆλ‹€. κ·Έλž˜μ„œ direction에 λͺ¨λ‘ previous λ©”μ„œλ“œκ°€ 생겼닀. ν•œλ° λ‹€μŒ 번 goStraightλ₯Ό ν•  λ•ŒλŠ” 이미 μ΄λ™ν–ˆλ˜ 기둝이 λ‚¨μ•„μžˆκ²Œ λ˜μ—ˆλ‹€. κ·Έλž˜μ„œ 맀번 goStraightλ₯Ό ν•  λ•Œλ§ˆλ‹€ λ§ˆμ§€λ§‰ 이동 기둝을 μ‚­μ œν–ˆλ‹€. κ·ΈλŸ¬λ‹€λ³΄λ‹ˆ board크기가 1일 κ²½μš°λŠ” μ΄λ™ν•œ 기둝이 λͺ¨λ‘ μ§€μ›Œμ Έλ²„λ¦¬λŠ” 것이 μ•„λ‹Œκ°€. μ‘°μž‘ν•˜μ§€λ§Œ μ˜ˆμ™Έ 처리λ₯Ό ν•΄μ£Όμ—ˆλ‹€.

경계쑰건이 μ°Έ λ―Έλ¬˜ν•˜λ‹€λŠ” 것을 λŠκΌˆλ‹€. μ‹œμž‘κ³Ό 끝을 μ–΄λ–»κ²Œ ν•  것인가? ν•œμ°Έμ„ ν—€λ§€λ‹€λ³΄λ‹ˆ 더 λ‚˜μ€ 방법이 μžˆλŠ”λ°λ„ 찾질 λͺ»ν•˜λŠ” 것 κ°™λ‹€.

μ½”λ“œ μ•ˆμ—μ„œ ν—€λ§€κΈ° λ³΄λ‹€λŠ” μ •ν™•νžˆ 생각을 μ •λ¦¬ν•΄μ„œ κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€. 이것 해보고 저것 ν•΄λ³΄λŠ” 사이에 μ‹œκ°„μ΄ λ„ˆλ¬΄ 많이 ν˜λŸ¬κ°”λ‹€. 결ꡭ에 닡은 λ‚˜μ™”μ§€λ§Œ, 이보닀 빨리 ν•  수 μžˆμ„ 것이닀.


~cpp

import unittest

ROW = 0
COL = 1

class Direction:
    pass
class Down(Direction):
    def next(self, coordinate):
        return (coordinate[ROW] + 1, coordinate[COL])
    def previous(self, coordinate):
        return Up().next( coordinate )
    
class Up( Direction ):
    def next(self, coordinate):
        return (coordinate[ROW] - 1, coordinate[COL])
    def previous(self, coordinate):
        return Down().next( coordinate )

class Right(Direction):
    def next(self, coordinate):
        return (coordinate[ROW], coordinate[COL] + 1 )
    def previous(self, coordinate):
        return Left().next( coordinate )

class Left(Direction):
    def next(self, coordinate):
        return (coordinate[ROW], coordinate[COL] - 1 )
    def previous(self, coordinate):
        return Right().next( coordinate )


class Board:
    def __init__(self, initialSize):
        self.size = initialSize
        self.setBoundary( 0, self.size )
    def setBoundary( self, start, end ):
        self.start = start
        self.end = end
    def isWall( self, coordinate ):
        if ( coordinate[ROW] < self.start ):
            return True
        elif ( coordinate[ROW] >= self.end ):
            return True
        elif ( coordinate[COL] < self.start ):
            return True
        elif ( coordinate[COL] >= self.end ):
            return True
        return False
    def decreaseBoundary(self, amount):
        self.setBoundary( self.start+amount, self.end-amount )
    

class Mover:
    def __init__(self):
        self.coordinate = (0,0)
        self.history = []
    def position(self):
        return self.coordinate
    def goStraight(self, direction, board):
        while ( not board.isWall( self.coordinate ) ):
            self.history.append( Point( self.coordinate, self.moveCount() ) )
            self.coordinate = direction.next( self.coordinate )
        self.coordinate = direction.previous( self.coordinate )
    def mark(self, point, value):
        point.value = value
    def moveCount(self):
        return len( self.history ) + 1
    def setPosition(self, coordinate):
        self.coordinate = coordinate
    def getHistory(self):
        return self.history
    def turnRound(self, board):
        for direction in [Right(), Down(), Left()]:
            self.goStraight( direction, board )
            self.eraseLastMovement()
        self.goStraight( Up(), board )
        if ( board.end - board.start != 1 ):
            self.eraseLastMovement()
        self.setPosition( self.nextFloor() )
    def nextFloor(self):
        return (self.coordinate[ROW]+1, self.coordinate[COL]+1 )
    def eraseLastMovement(self):
        self.history.pop()
    def isFinished( self, board ):
        return board.size * board.size < self.moveCount()
    def lastHistory( self ):
        return self.history[-1]
            
        
class Point:
    def __init__(self, coordinate, value):
        self.coordinate = coordinate
        self.value = value

class Array:
    def __init__(self, size):
        self.matrix = []
        for i in range(size):
            self.matrix.append( [Point((-1,-1),-1)] * size )
    def get(self, row, col):
        return self.matrix[row][col]
    def construct(self, pointList):
        for p in pointList:
            self.matrix[p.coordinate[ROW]][p.coordinate[COL]] = p
    def printValues(self):
        for row in self.matrix:
            for point in row:
                print point.value,
                print "\t",
            print

class SpiralArrayTest(unittest.TestCase):
    def setUp(self):
        self.size = 10
        self.board = Board(self.size)
        self.board.setBoundary(start=0, end=self.size)
        self.mover = Mover()
        self.array = Array( self.size )


class DirectionTest(SpiralArrayTest):
    def testNext(self):
        self.assertEquals( (1,0), Down().next( (0,0) ) )

    def testPrevious(self):
        self.assertEquals( (0,0), Down().previous( (1,0) ) )

class BoardTest(SpiralArrayTest):
    def testIsWall(self):
        self.assertEquals( False, self.board.isWall( (0,0) ) )
        self.assertEquals( True, self.board.isWall( (-1,0) ) )
        self.assertEquals( True, self.board.isWall( (0,-1) ) )
        self.assertEquals( True, self.board.isWall( (0,self.size) ) )
        self.assertEquals( True, self.board.isWall( (self.size,0) ) )

class MoverTest(SpiralArrayTest):
    def testGoStraightRight(self):
        self.mover.goStraight(Right(), self.board)
        self.assertEquals( (0,self.size-1), self.mover.position() )

    def testStorePointCoordinate(self):
        self.mover.goStraight( Down(), self.board )
        points = self.mover.getHistory()
        self.assertEquals( (self.size-1,0), points.pop().coordinate )
        self.assertEquals( (self.size-2,0), points.pop().coordinate )
    def testStoreMoveCount(self):
        self.mover.setPosition((self.size-1, self.size-1))
        self.mover.goStraight( Left(), self.board )
        points = self.mover.getHistory()
        self.assertEquals( self.size, points.pop().value )
        self.assertEquals( self.size-1, points.pop().value )
    def testStoreMovementWhenTurnRoundAndBoudaryNotOne(self):
        self.mover.setPosition( (0,0) )
        self.mover.turnRound(self.board)
        points = self.mover.getHistory()
        self.assertEquals( (self.size-1) * 4 , len( points ) )
    def testStoreMovementWhenTurnRoundAndBoudaryOne(self):
        self.board = Board( initialSize = 1 )
        self.mover.setPosition( (0,0) )
        self.mover.turnRound(self.board)
        points = self.mover.getHistory()
        self.assertEquals( 1 , len( points ) )
    def testTurnRoundWithBoundary0(self):
        self.mover.setPosition( (0,0) )
        self.mover.turnRound(self.board)
        self.assertEquals( (1,0), self.mover.lastHistory().coordinate )
    def testIsFinished(self):
        self.board = Board( initialSize = 1 )
        self.assertEquals( False, self.mover.isFinished( self.board ) )
        self.mover.turnRound( self.board )
        self.assertEquals( True, self.mover.isFinished( self.board ) )
    def testRun(self):
        self.board = Board( initialSize = 3 )
        self.mover.setPosition( (0,0) )
        self.mover.turnRound(self.board)
        self.assertEquals( (1,0), self.mover.lastHistory().coordinate )
        self.assertEquals( (1,1), self.mover.position() )

        self.board.decreaseBoundary(1)
        self.mover.turnRound(self.board)
        self.assertEquals( (1,1), self.mover.lastHistory().coordinate )

class ArrayTest(SpiralArrayTest):    
    def testConstructArray(self):
        self.mover.setPosition( (0,0) )
        self.mover.turnRound(self.board)
        self.array.construct( self.mover.getHistory() )
        self.assertEquals( (0,0), self.array.get(0,0).coordinate )
        self.assertEquals( (self.size-1, self.size-1),
                           self.array.get(self.size-1, self.size-1).coordinate )
        

    
if __name__ == "__main__":
    unittest.main()
    inputSize = input()
    board = Board(inputSize)
    mover = Mover()
    mover.setPosition((0,0))
    while ( not mover.isFinished( board ) ):
        mover.turnRound(board)
        board.decreaseBoundary(1)
    array = Array(inputSize)
    array.construct(mover.getHistory())
    array.printValues()

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:28:05
Processing time 0.0194 sec