-- -- $Header: /Users/dap/lua/RCS/HanoiPageIO.lua,v 1.7 2007/02/11 11:13:25 dap Exp $ -- -- Hanoi - Towers of Hanoi diversion -- -- Author (a) 2007, Damon A Permezel. All bugs revered. -- module(..., package.seeall) local Hanoi = require"Hanoi":subclass { __name = 'Hanoi with PageIO', window = { attr = { font = { name = 'Monaco', size = 14 }, background = 'black', -- foreground = 'yellow', -- does not seem to work! }, }, } local clearance = 0 local colors = {} table.foreach({ "red", "orange", "yellow", "green", "blue", "purple", "brown", "cyan", "gray", "magenta", "white" }, function (i,c) table.insert(colors, i, c) end) -- -- init - initialise a Hanoi object instance -- function Hanoi:init() Hanoi:super().init(self) self.pag = require"PageIO":new{key='hanoi'} local n = self.nRings self.flyRow = 5 self.baseRow = self.flyRow + n + 1 self.winWidth = 1 + 3*(2*n+1) + 2 + 4 + 1 self.winHeight = self.baseRow + 2 end -- -- moveRing - default method to move ring -- function Hanoi:moveRing(n, from, to) self:debug('move ring %d from %s to %s', n, from.name, to.name) to:push(from:pop()) self:debug() end -- -- reset - reset the page to the beginning state -- function Hanoi:reset() math.randomseed(os.time()) self:resetScreen() self:resetPoles() return self.nRings, self.poles.A, self.poles.B, self.poles.C end -- -- resetScreen - sic -- function Hanoi:resetScreen() local n, w, h = self.nRings, self.winWidth, self.winHeight local attr = self.window.attr local p = self.pag p:seek(0); p:deleteToEOP() attr.background = 'black' p:insert(('|' .. (" "):rep(w-3) .. '|\n'):rep(h-1), attr) attr.background = 'yellow' p:insert( '+' .. (" "):rep(w-3) .. "+\n", attr) self.debugStart = p:tell() end -- -- resetPoles - sic -- function Hanoi:resetPoles() local n = self.nRings local off = 1 -- border width local delta = 1 + n + 1 + n -- base separation local Pole = OO.class{ floor = self.baseRow, flyRow = self.flyRow, } function Pole:init() self.rows = {} self.depth = 0 end function Pole:push(ring) assert(not ring.pole) table.insert(self.rows, ring) ring.pole = self self.depth = self.depth + 1 if (ring.hanoi.optim) then -- gain sufficient 'height' so we don't 9/11 if (ring.row > self.floor - clearance) then ring:toRow(self.floor - clearance) end if self.prev and self.prev.prev == ring.last then if (ring.row > self.prev.floor) then ring:toRow(self.prev.floor) end end if self.next and self.next.next == ring.last then if (ring.row > self.next.floor) then ring:toRow(self.next.floor) end end end ring:toCol(self.col) ring:toRow(self.floor) self.floor = self.floor - 1 ring.last = nil end function Pole:pop() assert(self.depth > 0) ring = table.remove(self.rows) assert(ring.pole == self) assert(ring.row == self.floor+1) if (ring.hanoi.fancy) then -- let push() handle it all elseif (ring.hanoi.optim) then ring:toRow(ring.row - clearance) else ring:toRow(self.flyRow) end ring.last = self ring.pole = nil self.floor = self.floor + 1 self.depth = self.depth - 1 return ring end self.poles = { A = Pole:new{ name='A', col = off + 0 * delta + delta / 2 }, B = Pole:new{ name='B', col = off + 1 * delta + delta / 2 }, C = Pole:new{ name='C', col = off + 2 * delta + delta / 2 }, } if self.optim then self.poles.A.next = self.poles.B self.poles.B.next = self.poles.C self.poles.C.prev = self.poles.B self.poles.B.prev = self.poles.A end local Ring = self:resetRings() for i = n, 1, -1 do local ring = Ring:new{ n=i, row=0, col=1+i } if math.random(0, 1) == 1 then ring.col = self.winWidth - i - 4 end ring:draw() self.poles.A:push(ring) end end -- reset the rings, like totally, dude! function Hanoi:resetRings() local Ring = OO.class{ hanoi = self, flyRow = self.flyRow, baseRow = self.baseRow, winWidth = self.winWidth, n = 0, color = 'wild-mountain-peach', pag = self.pag, nRings = 0, haccel = 6, vaccel = 4, } function Ring:init() self.nRings = self.nRings + 1 self.color = colors[1 + (self.n % #colors)] end function Ring:seek() local n, w, p = self.nRings, self.winWidth, self.pag -- seek to the left edge of the ring p:seek(self.row * (w) + 1 + n - self.n + self.col) end function Ring:draw() local p = self.pag local attr = self.hanoi.window.attr attr.background = self.color self:seek() p:replace(("%1d"):format(self.n):rep(2*self.n+1), attr) p:display() -- os.execute('sleep 1') end function Ring:erase() local p = self.pag local attr = self.hanoi.window.attr attr.background = 'black' self:seek() p:replace((' '):format(self.n):rep(2*self.n+1), attr) end function Ring:toCol(col) local p = self.pag while self.col < col do self:erase() if (col - self.col > self.haccel) then self.col = self.col + self.haccel else self.col = self.col + 1 end self:draw() end while self.col > col do self:erase() if (self.col - col > self.haccel) then self.col = self.col - self.haccel else self.col = self.col - 1 end self:draw() end end function Ring:toRow(row) local p = self.pag while self.row < row do self:erase() if (row - self.row > self.vaccel) then self.row = self.row + self.vaccel else self.row = self.row + 1 end self:draw() end while self.row > row do self:erase() if (self.row - row > self.vaccel) then self.row = self.row - self.vaccel else self.row = self.row - 1 end self:draw() end end return Ring end function Hanoi:debug(fmt, ...) if not self.debugStart then return end local p = self.pag if not fmt then p:seek(self.debugStart) p:deleteToEOP() else p:seek(-1) p:insert((fmt .. '\n'):format(unpack(arg))) p:display() end end return Hanoi