continuity

curses game inspired by continuitygame.com
git clone https://a3nm.net/git/continuity/
Log | Files | Refs | README

commit cd585b34938b53c43ab79b817acd8f1ea93af5c1
parent da08927a85bbaf51133c1a1781733e88b22e6ccc
Author: Antoine Amarilli <ant.amarilli@free.fr>
Date:   Tue, 17 May 2011 00:38:31 -0400

added files

Diffstat:
README | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
continuity.py | 435+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
level | 27---------------------------
level1 | 27+++++++++++++++++++++++++++
level2 | 164+++++++++++++++----------------------------------------------------------------
level2.best | 10----------
level3 | 26++++++++++++++++++++++++++
level4 | 42++++++++++++++++++++++++++++++++++++++++++
level5 | 26++++++++++++++++++++++++++
level6 | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
levelb | 30------------------------------
levelc | 26--------------------------
leveld | 42------------------------------------------
levele | 26--------------------------
main.py | 423-------------------------------------------------------------------------------
15 files changed, 799 insertions(+), 718 deletions(-)

diff --git a/README b/README @@ -0,0 +1,79 @@ +continuity -- a curses game inspired by continuitygame.com +by Antoine Amarilli +licence: public domain + +This game is quite similar to the game at +<http://continuitygame.com/playcontinuity.html>. However, this is an +independent creation and is not endorsed by continuitygame.com. + +There are a few differences, though: +- python+curses rather than flash +- turn-based, not realtime +- no jumping but ladders +- not continuous but discrete +- falling from a block with no suitable block underneath isn't deadly +- no music +- only 6 levels +- score +- source code available and public domain + +== Requirements == + +- Python3 +- Curses bindings +- Color support on your terminal is recommended + +== How to play == + +$ ./continuity.py LEVEL + +LEVEL is the path to the file containing the level to play. + +=== Controls === + +- hjkl to move player +- HJKL to move blocks +- q to quit + +=== Elements === + +- '@': the player +- '*': grab all these items to open the exit +- '^': reach this exit to win +- '#': an obstacle you can walk on +- '%': a ladder which you can walk on and climb + +You can go from square to square with player movement keys. You can go +from block to block if the two blocks are adjacent and their boundaries +match exactly ('?' indicates a mismatch). If the boundaries do not +match, you cannot pass. You can walk on mismatched boundaries. + +=== Scoring === + +- Moves is the total number of player movement commands +- Dist is the total number of squares travelled by player (including + free falls) +- Swaps is the total number of block movement commands + +== Levels == + +=== Levels provided === + +Provided levels are inspired by some continuitygame.com levels. Levels 1 +to 5 are easy. Level 6 is harder. A non-optimal score for level 6 is 12 +moves, 44 dist, 70 swaps. + +=== Writing your own levels === + +Level files are mostly simple text files with a block by block ascii art +drawing of the level along with more info. level1 contains helpful +comments to understand what's going on. + +== TODO == + +- Support non-square blocks (partially coded) +- Support multiple levels (ie. running them in order) +- Better format for levels +- Code cleanup +- Design more levels + diff --git a/continuity.py b/continuity.py @@ -0,0 +1,435 @@ +#!/usr/bin/python3 +# continuity -- a curses game inspired by continuitygame.com +# by Antoine Amarilli +# licence: public domain + +import sys +import curses + +class Square: + wall, ladder, empty, exit, player, blank, key = range(7) + asciiMap = {wall: '#', ladder:'%', empty:' ', exit:'^', player:'@', + blank:'!', key:'*'} + + def __init__(self, parent, type, pos): + self.parent = parent + self.type = type + self.pos = pos + + def getNeighbour(self, dir): + return self.parent.getSquare((self.pos[0] + dir[0], self.pos[1] + dir[1])) + + def getType(self): + return self.type + + def setType(self, type): + self.type = type + + def render(self, pad, x, y): + if (self.parent.parent.getPlayer().getPos() == self): + self.parent.parent.getPlayer().setGraphPos((x + self.pos[0],y + + self.pos[1])) + c = '@' + mode = curses.A_BOLD + curses.A_BLINK + curses.color_pair(1) + else: + if (self.parent.parent.getPlayer().getPos().getParent() == self.parent): + mode = curses.A_BOLD + else: + mode = 0 + c = Square.asciiMap[self.type] + if (c == '^'): + if (self.parent.parent.keysLeft() == True): + mode = curses.A_BOLD + curses.color_pair(8) + else: + mode = curses.A_BOLD + curses.color_pair(2) + if (c == '%'): mode = curses.color_pair(3) + if (c == '#'): mode = curses.color_pair(4) + if (c == '!'): + mode = curses.color_pair(5) + pad.addch( x + self.pos[0],y + self.pos[1], c, mode) + c = ' ' + if (c == '*'): mode = curses.A_BOLD + curses.color_pair(2) + try: + pad.addch( x + self.pos[0],y + self.pos[1], c, mode) + except curses.error: + pass + + def getParent(self): + return self.parent + + +class Block: + left, right, top, bottom = range(4) + def __init__(self, parent, h, w, pos): + self.parent = parent + self.h = h + self.w = w + self.pos = pos + self.map = [] + for i in range(0, h): + self.map.append([]) + for j in range(0, w): + self.map[i].append(Square(self, 0, (0, 0))) + + def getBorder(self, t): + a = [] + if (t == Block.left): + for i in range(0, self.h): + a.append(self.getSquare((i,0)).getType()) + if (t == Block.right): + for i in range(0, self.h): + a.append(self.getSquare((i, self.w-1)).getType()) + if (t == Block.top): + for i in range(0, self.w): + a.append(self.getSquare((0, i)).getType()) + if (t == Block.bottom): + for i in range(0, self.w): + a.append(self.getSquare((self.h-1, i)).getType()) + return a + + def getNeighbour(self, dir): + return self.parent.getBlock((self.pos[0] + dir[0], self.pos[1] + dir[1])) + + def setSquare(self, x, y, s): + self.map[x][y] = s + + def getSquare(self, p): + x = p[0] + y = p[1] + if (x < 0): + a = self.getNeighbour((-1, 0)) + if (self.getBorder(Block.top) != a.getBorder(Block.bottom)): + return BlockSentry(self.parent, 0, 0, (0, 0)).getSquare((0,0)) + return a.getSquare((a.getH() + p[0], p[1])) + elif (y < 0): + a = self.getNeighbour((0, -1)) + if (self.getBorder(Block.left) != a.getBorder(Block.right)): + return BlockSentry(self.parent, 0, 0, (0, 0)).getSquare((0,0)) + return a.getSquare((p[0], a.getW() + p[1])) + elif (x >= self.getH()): + a = self.getNeighbour((1, 0)) + if (self.getBorder(Block.bottom) != a.getBorder(Block.top)): + return BlockSentry(self.parent, 0, 0, (0, 0)).getSquare((0,0)) + return a.getSquare((p[0] - self.getH(), p[1])) + elif (y >= self.getW()): + a = self.getNeighbour((0, 1)) + if (self.getBorder(Block.right) != a.getBorder(Block.left)): + return BlockSentry(self.parent, 0, 0, (0, 0)).getSquare((0,0)) + return a.getSquare((p[0], p[1] - self.getW())) + else: + return self.map[x][y] + + def getW(self): + return self.w + + def getH(self): + return self.h + + def render(self, pad, x, y): + for i in self.map: + for j in i: + j.render(pad, x, y) + + def loadFromFile(self, f): + for i in range(0, self.getH()): + l = f.readline() + for j in range(0, self.getW()): + x = l[j] + c = {v:k for k, v in Square.asciiMap.items()}[x] + if (x == '*'): + self.parent.addKey() + if (x == '@'): + c = Square.empty + self.map[i][j] = Square(self, c, (i, j)) + if (x == '@'): + self.parent.getPlayer().setPos(self.map[i][j]) + + + def move(self, p): + self.pos = (self.pos[0] + p[0], self.pos[1] + p[1]) + + + +class BlockSentry(Block): + def getSquare(self, p): + return Square(self, Square.blank, (0,0)) + + def render(self, pad, x, y): + for i in range(0, self.h): + for j in range(0, self.w): + (Square(self, Square.blank, (i,j))).render(pad, x, y) + +class Player: + keyMap = {(0, -1): 'h', (1, 0):'j', (-1, 0):'k', (0, 1): 'l'} + + def __init__(self, parent, square): + self.parent = parent + self.pos = square + self.graphPos = (0,0) + + def render(self, pad): + pad.move(self.graphPos[0], self.graphPos[1]) + + def setGraphPos(self, p): + self.graphPos = p + + def move(self, direction, volont): + a = self.pos.getNeighbour(direction) + if (a.getType() != Square.wall and a.getType() != Square.blank): + self.pos = a + self.parent.addDist() + if (volont == True): self.parent.addMoves() + if (self.pos.getType() == Square.key): + self.parent.removeKey() + self.pos.setType(Square.empty) + if (self.pos.getType() == Square.exit and not self.parent.keysLeft()): + return True + else: + return self.fall() + + def getPos(self): + return self.pos + + def setPos(self, pos): + self.pos = pos + + def fall(self): + a = self.pos.getType() + b = self.pos.getNeighbour((1,0)).getType() + while (self.pos.getType() != Square.ladder and ( + self.pos.getNeighbour((1,0)).getType() == Square.empty or + self.pos.getNeighbour((1,0)).getType() == Square.key or + self.pos.getNeighbour((1,0)).getType() == Square.exit)): + if self.move((1, 0), 0): + return True + return False + + +class Level: + codeMap = {104: 'h', 106:'j', 107:'k', 108: 'l', 113: 'q', 72: 'H', + 74:'J', 75: 'K', 76:'L'} + def __init__(self, h, w, x, y): + self.h = h + self.w = w + self.holeX = x + self.holeY = y + self.map = [] + self.keys = 0 + self.cswap = 0 + self.cmove = 0 + self.cdist = 0 + for i in range(0, h): + self.map.append([]) + for j in range(0, w): + self.map[i].append(Block(0, 0, 0, 0)) + self.player = Player(self, Square(0, 0, (0,0))) + + def loadFromFile(self, f): + bh = int(f.readline()) + bw = int(f.readline()) + for i in range(0, self.h): + for j in range(0, self.w): + if ((i, j) != (self.holeX, self.holeY)): + self.map[i][j] = Block(self, bh, bw, (i, j)) + else: + self.map[i][j] = BlockSentry(self, bh, bw, (i, j)) + self.map[i][j].loadFromFile(f) + + def addKey(self): + self.keys+=1 + + def addDist(self): + self.cdist += 1 + + def addMoves(self): + self.cmove += 1 + + def addSwap(self): + self.cswap += 1 + + def removeKey(self): + self.keys-=1 + + def keysLeft(self): + return self.keys != 0 + + def getPlayer(self): + return self.player + + def render(self, pad): + m = 0 + for i in range(0, self.w): + m = m + self.map[0][i].getW() + 1 + for i in range(0, m+1): + pad.addch( 0 ,i, '\'', curses.color_pair(5)) + pad.addch( 0 ,i, ' ', curses.color_pair(5)) + x = 0 + for j in range(0, self.h): + y = 0 + for i in range(0, self.map[0][j].getH()+1): + pad.addch( j*(self.map[0][j].getH()+1)+1 +i ,0, ' ', curses.color_pair(5)) + for i in range(0, self.w): + a = self.map[j][i] + a.render(pad, j*a.getH()+1+j, i*a.getW()+1+i) + l = a.getBorder(Block.bottom) + l2 = a.getNeighbour((1, 0)).getBorder(Block.top) + if (l != l2): + for k in range(0, a.getW()+1): + if (l2 != [] and l != [] and l[k-1] != l2[k-1]): + pad.addch(x+self.map[0][j].getH() +1,y+k, + '?', curses.color_pair(5)) + else: + pad.addch( x+self.map[0][j].getH() +1,y+k, + '\'', curses.color_pair(5)) + pad.addch( x+self.map[0][j].getH() +1,y+k, + ' ', curses.color_pair(5)) + else: + for k in range(0, a.getW()): + if (l[k] == Square.wall): + pad.addch( x+self.map[0][j].getH()+1,y+k+1, + '|', curses.color_pair(6)) + else: + pad.addch( x+self.map[0][j].getH()+1,y+k+1, + '\'', curses.color_pair(7)) + pad.addch( x+self.map[0][j].getH()+1,y+k+1, + ' ', curses.color_pair(7)) + y = y + a.getW() + 1 + l = a.getBorder(Block.right) + l2 = a.getNeighbour((0, 1)).getBorder(Block.left) + if (l != l2): + for k in range(0, a.getH()+1): + if (l2 != [] and l != [] and l[k-1] != l2[k-1]): + pad.addch( x+k ,y, '?', curses.color_pair(5)) + else: + pad.addch( x+k ,y, '\'', curses.color_pair(5)) + pad.addch( x+k ,y, ' ', curses.color_pair(5)) + else: + for k in range(0, a.getH()): + if (l[k] == Square.wall): + pad.addch( x+k+1 ,y, '-', curses.color_pair(6)) + else: + pad.addch( x+k+1 ,y, '\'', curses.color_pair(7)) + pad.addch( x+k+1 ,y, ' ', curses.color_pair(7)) + for k in range(0, a.getH()+1): + pad.addch( x+k ,y, '\'', curses.color_pair(5)) + pad.addch( x+k ,y, ' ', curses.color_pair(5)) + x = x + self.map[0][j].getH() + 1 + m = 0 + for i in range(0, self.h): + m = m + self.map[i][0].getH() + 1 + for i in range(0, y+1): + pad.addch( m ,i, '\'', curses.color_pair(5)) + pad.addch( m ,i, ' ', curses.color_pair(5)) + x = 0 + for j in range(0, self.h+1): + y = 0 + for i in range(0, self.w+1): + pad.addch(x, y, '+', curses.color_pair(9)) + y += self.getBlock((min(j, self.h-1), i)).getW() + 1; + x += self.getBlock((j, self.w-1)).getH() + 1; + + pad.addstr(self.scoreRep()) + self.player.render(pad) + pad.refresh() + + def scoreRep(self): + return "Moves: %3d\nDist: %4d\nSwaps: %3d" % ( + self.cmove, self.cdist, self.cswap ) + + def getH(self): + return self.h + + def getW(self): + return self.w + + def getBlock(self, p): + x = p[0] + y = p[1] + if (x < 0 or y < 0 or x >= self.getH() or y >= self.getW()): + return BlockSentry(self, 0, 0, (0,0)) + else: + return self.map[x][y] + + def move(self, p): + p2 = (-p[0], -p[1]) + tx = p2[0]+self.holeX + ty = p2[1]+self.holeY + t = self.getBlock((tx, ty)) + if (type(t) != BlockSentry): + self.map[tx][ty] = self.map[self.holeX][self.holeY] + self.map[self.holeX][self.holeY] = t + t.move(p) + self.map[tx][ty].move(p2) + self.holeX = tx + self.holeY = ty + self.addSwap() + return self.player.fall() + +if (len(sys.argv) != 2): + print("Usage: %s LEVEL" % sys.argv[0]) + sys.exit(1) + +try: + f = open(sys.argv[1]) +except IOError as e: + print("Cannot access %s: %s" % (sys.argv[1], e.strerror)) + sys.exit(1) + + + +stdscr = curses.initscr() +curses.noecho() + +h = int(f.readline()) +w = int(f.readline()) +x = int(f.readline()) +y = int(f.readline()) +l = Level(h, w, x, y) +l.loadFromFile(f) + +pad = curses.newwin(h*(l.getBlock((0,0)).getH()+1)+4, + w*(l.getBlock((0,0)).getW()+1)+1) + +curses.start_color() +curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK) +curses.init_pair(2, curses.COLOR_RED, curses.COLOR_YELLOW) +curses.init_pair(3, curses.COLOR_GREEN, curses.COLOR_BLACK) +curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLUE) +curses.init_pair(5, curses.COLOR_BLACK, curses.COLOR_RED) +curses.init_pair(6, curses.COLOR_RED, curses.COLOR_BLUE) +curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK) +curses.init_pair(8, curses.COLOR_YELLOW, curses.COLOR_BLACK) +curses.init_pair(9, curses.COLOR_MAGENTA, curses.COLOR_MAGENTA) + +exit = False +won = False + +stdscr.refresh() + +while (not exit): + l.render(pad) + try: input = Level.codeMap[stdscr.getch()] + except KeyError: + input = '' + pass + if (input == 'q'): + exit = True + elif (input == input.upper()): + try: won = l.move({v:k for k, v in Player.keyMap.items()}[input.lower()]) + except KeyError: pass + else: + try: won = l.getPlayer().move({v:k for k, v in + Player.keyMap.items()}[input], 1) + except KeyError: pass + exit = exit or won + +curses.nocbreak(); stdscr.keypad(0); curses.echo() + +curses.endwin() + +if (won): + print ("Congratulations, you won!\n") + print ("Your score was:") + print (l.scoreRep()) +else: print ("Bye.") + diff --git a/level b/level @@ -1,27 +0,0 @@ -2 -2 -0 -1 -5 -5 -##### Level syntax tips -#@ # First line is number of block lines -###% Second line is number of block cols -#* % Third line is line position of empty block -##### Fourth line is col position of empty block - Fifth line is width of blocks - Sixth line is height of blocks - Following lines are description of blocks one after the other - Blocks will be assigned left to right and then top to bottom - (ie. give the blocks for the first line, then second line, etc.) -##### If there are too many characters on a line, they get ignored -# %% You can use this to write comments (like these) - % # The following sprites are allowed: - %# # - ' ' is empty space (player falls thru, can walk thru) -##### - '#' is a wall (player can walk over, can't pass thru) -##### - '%' is a ladder (player can walk over, can climb over) - ^# - '*' is a key (collect all to open the exit -#%### - '^' is an exit (reach any with all keys to win) -#% *# - '@' is the player's start position (only one allowed, will be empty space when player leaves it) -##### PS: you must give a block description for the empty block, it - will be ignored diff --git a/level1 b/level1 @@ -0,0 +1,27 @@ +2 +2 +0 +1 +5 +5 +##### Level syntax tips +#@ # First line is number of block lines +###% Second line is number of block cols +#* % Third line is line position of empty block +##### Fourth line is col position of empty block + Fifth line is width of blocks + Sixth line is height of blocks + Following lines are description of blocks one after the other + Blocks will be assigned left to right and then top to bottom + (ie. give the blocks for the first line, then second line, etc.) +##### If there are too many characters on a line, they get ignored +# %% You can use this to write comments (like these) + % # The following sprites are allowed: + %# # - ' ' is empty space (player falls thru, can walk thru) +##### - '#' is a wall (player can walk over, can't pass thru) +##### - '%' is a ladder (player can walk over, can climb over) + ^# - '*' is a key (collect all to open the exit) +#%### - '^' is an exit (reach any with all keys to win) +#% *# - '@' is the player's start position (only one allowed, will be empty space when player leaves it) +##### PS: you must give a block description for the empty block, it + will be ignored diff --git a/level2 b/level2 @@ -1,134 +1,30 @@ -4 -4 -3 -3 -8 -8 - ## - ## - @ ## ^ - # ## # - ## - ## - ## - ## end - ## - # - ##### - - - - - ## end - - - - -### ### -### ### - - end - ## - ## - ## - ## - ## ## - ## ## - ## - ## End - ## - ## - *## - ### - ## # - ## - ## - ## end - - - - %%%%## -# -# - - end - - - - ##%%%% - # - # - - end - ## - ## - ## - ## -######## -######## - - End - - - % % - # # ## - - - - end - ## - -### - # - ###### - ###### - - end - - - % % - # # ## - - - - end - ## - ## - ## - ## -## ## -## ## - ## - ## End - - - - -###### -###### - - end - ## - ## - ## - ## - ## ## - ## ## - ## - ## end - - * - %##% - %####% - ###### - - - END - BLANK - BLANK - BLANK - BLANK - BLANK - BLANK - BLANK - BLANK +2 +2 +0 +1 +6 +6 +###### +# @ +###### +# +# ^ +###### + + + + + + +###### + +###### + ### + +###### +###### + ## +# ## # +# ## # + ## +###### diff --git a/level2.best b/level2.best @@ -1,10 +0,0 @@ - Your score was: - Moves: 14 - Dist: 62 - Swaps: 55 - - -Your score was: - Moves: 20 - Dist: 52 - Swaps: 79 diff --git a/level3 b/level3 @@ -0,0 +1,26 @@ +2 +2 +1 +1 +5 +5 +##### +# @# +# ## +# # +### # +### # +# # +# ### +# # +# # # +# # # +# ^ # + #### +# +##### + + + + + diff --git a/level4 b/level4 @@ -0,0 +1,42 @@ +2 +3 +1 +2 +6 +8 +## ##### +## @ # +##### # +##### # +## # +## ##### +### # ## + # # ## +### # ## + # # + #### # +## ## # +## ##### +## +######## + # + # +### # ## +## ## # +## # +###### # +## # +## ^ # +## ##### +######## + #### +### #### + # + # +## ##### + + + + + + diff --git a/level5 b/level5 @@ -0,0 +1,26 @@ +2 +2 +1 +0 +5 +10 + + + + + # + + + ^ +@ +# + + + + + +# + + + + diff --git a/level6 b/level6 @@ -0,0 +1,134 @@ +4 +4 +3 +3 +8 +8 + ## + ## + @ ## ^ + # ## # + ## + ## + ## + ## end + ## + # + ##### + + + + + ## end + + + + +### ### +### ### + + end + ## + ## + ## + ## + ## ## + ## ## + ## + ## End + ## + ## + *## + ### + ## # + ## + ## + ## end + + + + %%%%## +# +# + + end + + + + ##%%%% + # + # + + end + ## + ## + ## + ## +######## +######## + + End + + + % % + # # ## + + + + end + ## + +### + # + ###### + ###### + + end + + + % % + # # ## + + + + end + ## + ## + ## + ## +## ## +## ## + ## + ## End + + + + +###### +###### + + end + ## + ## + ## + ## + ## ## + ## ## + ## + ## end + + * + %##% + %####% + ###### + + + END + BLANK + BLANK + BLANK + BLANK + BLANK + BLANK + BLANK + BLANK diff --git a/levelb b/levelb @@ -1,30 +0,0 @@ -2 -2 -0 -1 -6 -6 -###### -# @ -###### -# -# ^ -###### - - - - - - -###### - -###### - ### - -###### -###### - ## -# ## # -# ## # - ## -###### diff --git a/levelc b/levelc @@ -1,26 +0,0 @@ -2 -2 -1 -1 -5 -5 -##### -# @# -# ## -# # -### # -### # -# # -# ### -# # -# # # -# # # -# ^ # - #### -# -##### - - - - - diff --git a/leveld b/leveld @@ -1,42 +0,0 @@ -2 -3 -1 -2 -6 -8 -## ##### -## @ # -##### # -##### # -## # -## ##### -### # ## - # # ## -### # ## - # # - #### # -## ## # -## ##### -## -######## - # - # -### # ## -## ## # -## # -###### # -## # -## ^ # -## ##### -######## - #### -### #### - # - # -## ##### - - - - - - diff --git a/levele b/levele @@ -1,26 +0,0 @@ -2 -2 -1 -0 -5 -10 - - - - - # - - - ^ -@ -# - - - - - -# - - - - diff --git a/main.py b/main.py @@ -1,423 +0,0 @@ -#/usr/bin/python - - -import sys -import curses - - -class Square: - wall, ladder, empty, exit, player, blank, key = range(7) - asciiMap = {wall: '#', ladder:'%', empty:' ', exit:'^', player:'@', blank:'!', key:'*'} - - def __init__(self, parent, type, pos): - self.parent = parent - self.type = type - self.pos = pos - - def getNeighbour(self, dir): - return self.parent.getSquare((self.pos[0] + dir[0], self.pos[1] + dir[1])) - - def getType(self): - return self.type - - def setType(self, type): - self.type = type - - def render(self, pad, x, y): - if (self.parent.parent.getPlayer().getPos() == self): - self.parent.parent.getPlayer().setGraphPos((x + self.pos[0],y + self.pos[1])) - c = '@' - mode = curses.A_BOLD + curses.A_BLINK + curses.color_pair(1) - else: - if (self.parent.parent.getPlayer().getPos().getParent() == self.parent): - mode = curses.A_BOLD - else: - mode = 0 - c = Square.asciiMap[self.type] - if (c == '^'): - if (self.parent.parent.keysLeft() == True): - mode = curses.A_BOLD + curses.color_pair(8) - else: - mode = curses.A_BOLD + curses.color_pair(2) - if (c == '%'): mode = curses.color_pair(3) - if (c == '#'): mode = curses.color_pair(4) - if (c == '!'): - mode = curses.color_pair(5) - pad.addch( x + self.pos[0],y + self.pos[1], c, mode) - c = ' ' - if (c == '*'): mode = curses.A_BOLD + curses.color_pair(2) - try: - pad.addch( x + self.pos[0],y + self.pos[1], c, mode) - except curses.error: - pass - - def getParent(self): - return self.parent - - -class Block: - left, right, top, bottom = range(4) - def __init__(self, parent, h, w, pos): - self.parent = parent - self.h = h - self.w = w - self.pos = pos - self.map = [] - for i in range(0, h): - self.map.append([]) - for j in range(0, w): - self.map[i].append(Square(self, 0, (0, 0))) - - def getBorder(self, t): - a = [] - if (t == Block.left): - for i in range(0, self.h): - a.append(self.getSquare((i,0)).getType()) - if (t == Block.right): - for i in range(0, self.h): - a.append(self.getSquare((i, self.w-1)).getType()) - if (t == Block.top): - for i in range(0, self.w): - a.append(self.getSquare((0, i)).getType()) - if (t == Block.bottom): - for i in range(0, self.w): - a.append(self.getSquare((self.h-1, i)).getType()) - return a - - def getNeighbour(self, dir): - return self.parent.getBlock((self.pos[0] + dir[0], self.pos[1] + dir[1])) - - def setSquare(self, x, y, s): - self.map[x][y] = s - - def getSquare(self, p): - x = p[0] - y = p[1] - if (x < 0): - a = self.getNeighbour((-1, 0)) - if (self.getBorder(Block.top) != a.getBorder(Block.bottom)): - return BlockSentry(self.parent, 0, 0, (0, 0)).getSquare((0,0)) - return a.getSquare((a.getH() + p[0], p[1])) - elif (y < 0): - a = self.getNeighbour((0, -1)) - if (self.getBorder(Block.left) != a.getBorder(Block.right)): - return BlockSentry(self.parent, 0, 0, (0, 0)).getSquare((0,0)) - return a.getSquare((p[0], a.getW() + p[1])) - elif (x >= self.getH()): - a = self.getNeighbour((1, 0)) - if (self.getBorder(Block.bottom) != a.getBorder(Block.top)): - return BlockSentry(self.parent, 0, 0, (0, 0)).getSquare((0,0)) - return a.getSquare((p[0] - self.getH(), p[1])) - elif (y >= self.getW()): - a = self.getNeighbour((0, 1)) - if (self.getBorder(Block.right) != a.getBorder(Block.left)): - return BlockSentry(self.parent, 0, 0, (0, 0)).getSquare((0,0)) - return a.getSquare((p[0], p[1] - self.getW())) - else: - return self.map[x][y] - - def getW(self): - return self.w - - def getH(self): - return self.h - - def render(self, pad, x, y): - for i in self.map: - for j in i: - j.render(pad, x, y) - - def loadFromFile(self, f): - for i in range(0, self.getH()): - l = f.readline() - for j in range(0, self.getW()): - x = l[j] - c = {v:k for k, v in Square.asciiMap.items()}[x] - if (x == '*'): - self.parent.addKey() - if (x == '@'): - c = Square.empty - self.map[i][j] = Square(self, c, (i, j)) - if (x == '@'): - self.parent.getPlayer().setPos(self.map[i][j]) - - - def move(self, p): - self.pos = (self.pos[0] + p[0], self.pos[1] + p[1]) - - - -class BlockSentry(Block): - def getSquare(self, p): - return Square(self, Square.blank, (0,0)) - - def render(self, pad, x, y): - for i in range(0, self.h): - for j in range(0, self.w): - (Square(self, Square.blank, (i,j))).render(pad, x, y) - -class Player: - keyMap = {(0, -1): 'h', (1, 0):'j', (-1, 0):'k', (0, 1): 'l'} - - def __init__(self, parent, square): - self.parent = parent - self.pos = square - self.graphPos = (0,0) - - def render(self, pad): - pad.move(self.graphPos[0], self.graphPos[1]) - - def setGraphPos(self, p): - self.graphPos = p - - def move(self, direction, volont): - a = self.pos.getNeighbour(direction) - if (a.getType() != Square.wall and a.getType() != Square.blank): - self.pos = a - self.parent.addDist() - if (volont == True): self.parent.addMoves() - if (self.pos.getType() == Square.key): - self.parent.removeKey() - self.pos.setType(Square.empty) - if (self.pos.getType() == Square.exit and not self.parent.keysLeft()): - return True - else: - return self.fall() - - def getPos(self): - return self.pos - - def setPos(self, pos): - self.pos = pos - - def fall(self): - a = self.pos.getType() - b = self.pos.getNeighbour((1,0)).getType() - while (self.pos.getType() != Square.ladder and (self.pos.getNeighbour((1,0)).getType() == Square.empty or self.pos.getNeighbour((1,0)).getType() == Square.key or self.pos.getNeighbour((1,0)).getType() == Square.exit)): - if self.move((1, 0), 0): - return True - return False - - -class Level: - codeMap = {104: 'h', 106:'j', 107:'k', 108: 'l', 113: 'q', 72: 'H', 74:'J', 75: 'K', 76:'L'} - def __init__(self, h, w, x, y): - self.h = h - self.w = w - self.holeX = x - self.holeY = y - self.map = [] - self.keys = 0 - self.cswap = 0 - self.cmove = 0 - self.cdist = 0 - for i in range(0, h): - self.map.append([]) - for j in range(0, w): - self.map[i].append(Block(0, 0, 0, 0)) - self.player = Player(self, Square(0, 0, (0,0))) - - def loadFromFile(self, f): - bh = int(f.readline()) - bw = int(f.readline()) - for i in range(0, self.h): - for j in range(0, self.w): - if ((i, j) != (self.holeX, self.holeY)): - self.map[i][j] = Block(self, bh, bw, (i, j)) - else: - self.map[i][j] = BlockSentry(self, bh, bw, (i, j)) - self.map[i][j].loadFromFile(f) - - def addKey(self): - self.keys+=1 - - def addDist(self): - self.cdist += 1 - - def addMoves(self): - self.cmove += 1 - - def addSwap(self): - self.cswap += 1 - - def removeKey(self): - self.keys-=1 - - def keysLeft(self): - return self.keys != 0 - - def getPlayer(self): - return self.player - - def render(self, pad): - m = 0 - for i in range(0, self.w): - m = m + self.map[0][i].getW() + 1 - for i in range(0, m+1): - pad.addch( 0 ,i, '\'', curses.color_pair(5)) - pad.addch( 0 ,i, ' ', curses.color_pair(5)) - x = 0 - for j in range(0, self.h): - y = 0 - for i in range(0, self.map[0][j].getH()+1): - pad.addch( j*(self.map[0][j].getH()+1)+1 +i ,0, ' ', curses.color_pair(5)) - for i in range(0, self.w): - a = self.map[j][i] - a.render(pad, j*a.getH()+1+j, i*a.getW()+1+i) - l = a.getBorder(Block.bottom) - l2 = a.getNeighbour((1, 0)).getBorder(Block.top) - if (l != l2): - for k in range(0, a.getW()+1): - if (l2 != [] and l != [] and l[k-1] != l2[k-1]): - pad.addch( x+self.map[0][j].getH() +1,y+k, '?', curses.color_pair(5)) - else: - pad.addch( x+self.map[0][j].getH() +1,y+k, '\'', curses.color_pair(5)) - pad.addch( x+self.map[0][j].getH() +1,y+k, ' ', curses.color_pair(5)) - else: - for k in range(0, a.getW()): - if (l[k] == Square.wall): - pad.addch( x+self.map[0][j].getH()+1,y+k+1, '|', curses.color_pair(6)) - else: - pad.addch( x+self.map[0][j].getH()+1,y+k+1, '\'', curses.color_pair(7)) - pad.addch( x+self.map[0][j].getH()+1,y+k+1, ' ', curses.color_pair(7)) - y = y + a.getW() + 1 - l = a.getBorder(Block.right) - l2 = a.getNeighbour((0, 1)).getBorder(Block.left) - if (l != l2): - for k in range(0, a.getH()+1): - if (l2 != [] and l != [] and l[k-1] != l2[k-1]): - pad.addch( x+k ,y, '?', curses.color_pair(5)) - else: - pad.addch( x+k ,y, '\'', curses.color_pair(5)) - pad.addch( x+k ,y, ' ', curses.color_pair(5)) - else: - for k in range(0, a.getH()): - if (l[k] == Square.wall): - pad.addch( x+k+1 ,y, '-', curses.color_pair(6)) - else: - pad.addch( x+k+1 ,y, '\'', curses.color_pair(7)) - pad.addch( x+k+1 ,y, ' ', curses.color_pair(7)) - for k in range(0, a.getH()+1): - pad.addch( x+k ,y, '\'', curses.color_pair(5)) - pad.addch( x+k ,y, ' ', curses.color_pair(5)) - x = x + self.map[0][j].getH() + 1 - m = 0 - for i in range(0, self.h): - m = m + self.map[i][0].getH() + 1 - for i in range(0, y+1): - pad.addch( m ,i, '\'', curses.color_pair(5)) - pad.addch( m ,i, ' ', curses.color_pair(5)) - x = 0 - for j in range(0, self.h+1): - y = 0 - for i in range(0, self.w+1): - pad.addch(x, y, '+', curses.color_pair(9)) - y += self.getBlock((min(j, self.h-1), i)).getW() + 1; - x += self.getBlock((j, self.w-1)).getH() + 1; - - - pad.addstr( self.scoreRep()) - self.player.render(pad) - pad.refresh() - - def scoreRep(self): - return "Moves: %3d\nDist: %4d\nSwaps: %3d" %(self.cmove, self.cdist, self.cswap ) - - def getH(self): - return self.h - - def getW(self): - return self.w - - def getBlock(self, p): - x = p[0] - y = p[1] - if (x < 0 or y < 0 or x >= self.getH() or y >= self.getW()): - return BlockSentry(self, 0, 0, (0,0)) - else: - return self.map[x][y] - - def move(self, p): - p2 = (-p[0], -p[1]) - tx = p2[0]+self.holeX - ty = p2[1]+self.holeY - t = self.getBlock((tx, ty)) - if (type(t) != BlockSentry): - self.map[tx][ty] = self.map[self.holeX][self.holeY] - self.map[self.holeX][self.holeY] = t - t.move(p) - self.map[tx][ty].move(p2) - self.holeX = tx - self.holeY = ty - self.addSwap() - return self.player.fall() - - - - - - - - - - - -stdscr = curses.initscr() -curses.noecho() -curses.cbreak() -stdscr.keypad(1) - - -f = open(sys.argv[1]) -h = int(f.readline()) -w = int(f.readline()) -x = int(f.readline()) -y = int(f.readline()) -l = Level(h, w, x, y) -l.loadFromFile(f) - -pad = curses.newwin(h*(l.getBlock((0,0)).getH()+1)+4, w*(l.getBlock((0,0)).getW()+1)+1) - -curses.start_color() -curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK) -curses.init_pair(2, curses.COLOR_RED, curses.COLOR_YELLOW) -curses.init_pair(3, curses.COLOR_GREEN, curses.COLOR_BLACK) -curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLUE) -curses.init_pair(5, curses.COLOR_BLACK, curses.COLOR_RED) -curses.init_pair(6, curses.COLOR_RED, curses.COLOR_BLUE) -curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK) -curses.init_pair(8, curses.COLOR_YELLOW, curses.COLOR_BLACK) -curses.init_pair(9, curses.COLOR_MAGENTA, curses.COLOR_MAGENTA) - -exit = False -won = False - -stdscr.addstr ("Continuity\nGame\n\nNameless\ncurses\nclone\n\nPress\nany\nkey\n") - -while (not exit): - l.render(pad) - try: input = Level.codeMap[stdscr.getch()] - except KeyError: - input = '' - pass - if (input == 'q'): - exit = True - elif (input == input.upper()): - try: won = l.move({v:k for k, v in Player.keyMap.items()}[input.lower()]) - except KeyError: pass - else: - try: won = l.getPlayer().move({v:k for k, v in Player.keyMap.items()}[input], 1) - except KeyError: pass - exit = exit or won - -curses.endwin() - -curses.nocbreak(); stdscr.keypad(0); curses.echo() - - -if (won): - print ("Congratulations, you won!\n") - print ("Your score was:") - print (l.scoreRep()) -else: print ("Bye.\n") -