from cmu_graphics.cmu_graphics import app, AppWrapper from cmu_graphics import * from random import randint from time import sleep from sys import exit def onAppStart(app_inst: AppWrapper): app_inst.rows = 8 app_inst.cols = 10 app_inst.boardLeft = 25 app_inst.boardTop = 85 app_inst.boardWidth = 350 app_inst.boardHeight = 280 app_inst.cellBorderWidth = 1 app_inst.snake = [(randint(0, app_inst.rows - 1), randint(0, app_inst.cols - 1))] app_inst.direction = [0, 0] app_inst.fruit = None app_inst.gameOver = False app_inst.portals = [(-1, -1), (-1, -1)] makePortals(app_inst) app_inst.poison = None def makePortals(app_inst: AppWrapper): while ( manhattenDist(app_inst.portals[0], app_inst.portals[1]) < 5 or distanceToWall(app_inst, app_inst.portals[0]) < 2 or distanceToWall(app_inst, app_inst.portals[1]) < 2 ): app_inst.portals = [ (randint(0, app_inst.rows - 1), randint(0, app_inst.cols - 1)), (randint(0, app_inst.rows - 1), randint(0, app_inst.cols - 1)), ] def distanceToWall(app_inst: AppWrapper, pos): return min(pos[0], app_inst.rows - pos[0] - 1, pos[1], app_inst.cols - pos[1] - 1) def manhattenDist(p1, p2): return abs(p1[0] - p2[0]) + abs(p1[1] - p2[1]) def onKeyPress(app_inst: AppWrapper, key): if key == "up" and not app_inst.direction[0]: app_inst.direction = [-1, 0] elif key == "down" and not app_inst.direction[0]: app_inst.direction = [1, 0] elif key == "left" and not app_inst.direction[1]: app_inst.direction = [0, -1] elif key == "right" and not app_inst.direction[1]: app_inst.direction = [0, 1] elif key == "r": onAppStart(app_inst) elif key == "q": exit() elif key == "space": app_inst.gameOver = True def moveSnake(app_inst: AppWrapper): if app_inst.gameOver: return head = app_inst.snake[0] newHead = ( (head[0] + app_inst.direction[0]) % app_inst.rows, (head[1] + app_inst.direction[1]) % app_inst.cols, ) app_inst.snake.insert(0, newHead) if newHead in app_inst.portals: app_inst.snake[0] = app_inst.portals[1 - app_inst.portals.index(newHead)] elif newHead == app_inst.poison: app_inst.gameOver = True if len(app_inst.snake) > 1: app_inst.snake.pop() for segment in app_inst.snake[1:]: if app_inst.snake[0] == segment: app_inst.gameOver = True def onStep(app_inst: AppWrapper): if not app_inst.fruit: makeFruit(app_inst) makePoison(app_inst) head = app_inst.snake[0] if head == app_inst.fruit: app_inst.fruit = None app_inst.snake.append(app_inst.snake[-1]) moveSnake(app_inst) def getCell(app_inst: AppWrapper, pos): if pos in app_inst.snake: return "snake" elif pos == app_inst.fruit: return "fruit" elif pos in app_inst.portals: return "portal" elif pos == app_inst.poison: return "poison" elif pos == None: return "empty" return None def makeFruit(app_inst: AppWrapper): fruit = None while getCell(app_inst, fruit) is not None: fruit = (randint(0, app_inst.rows - 1), randint(0, app_inst.cols - 1)) app_inst.fruit = fruit break def makePoison(app_inst: AppWrapper): poison = None while getCell(app_inst, poison) is not None: poison = (randint(0, app_inst.rows - 1), randint(0, app_inst.cols - 1)) app_inst.poison = poison break def redrawAll(app_inst: AppWrapper): drawLabel("Snake!", 200, 30, size=16) drawBoard(app_inst) drawBoardBorder(app_inst) drawFruit(app_inst) drawPoison(app_inst) drawPortals(app_inst) drawSnake(app_inst) sleep(0.1875) if app_inst.gameOver: drawGameOver(app_inst) def drawPoison(app_inst: AppWrapper): if app_inst.poison is not None: cellLeft, cellTop = getCellLeftTop(app_inst, *app_inst.poison) cellWidth, cellHeight = getCellSize(app_inst) drawCircle( cellLeft + cellWidth / 2, cellTop + cellHeight / 2, cellWidth / 2 - 6, fill="purple", ) def drawPortals(app_inst: AppWrapper): for portal in app_inst.portals: cellLeft, cellTop = getCellLeftTop(app_inst, portal[0], portal[1]) cellWidth, cellHeight = getCellSize(app_inst) drawCircle( cellLeft + cellWidth / 2, cellTop + cellHeight / 2, cellWidth / 2 - 6, fill="black", ) def drawGameOver(app_inst: AppWrapper): drawRect(app_inst.width / 2 - 150, app_inst.height / 2 - 50, 300, 100, fill="white") drawLabel("Game Over!", app_inst.width / 2, app_inst.height / 2 - 20, size=16) drawLabel( f"Score: {len(app_inst.snake)-1}", app_inst.width / 2, app_inst.height / 2, size=12, ) drawLabel( "Press R to restart", app_inst.width / 2, app_inst.height / 2 + 20, size=12 ) drawLabel("Press Q to quit", app_inst.width / 2, app_inst.height / 2 + 40, size=12) def drawFruit(app_inst: AppWrapper): if app_inst.fruit is not None: cellLeft, cellTop = getCellLeftTop(app_inst, *app_inst.fruit) cellWidth, cellHeight = getCellSize(app_inst) drawCircle( cellLeft + cellWidth / 2, cellTop + cellHeight / 2, cellWidth / 2 - 8, fill="red", ) def drawSnake(app_inst: AppWrapper): cellWidth, cellHeight = getCellSize(app_inst) for i, segment in enumerate(app_inst.snake): cellLeft, cellTop = getCellLeftTop(app_inst, segment[0], segment[1]) color = "green" if i == 0 else "gray" drawCircle( cellLeft + cellWidth / 2, cellTop + cellHeight / 2, cellWidth / 2 - 4, fill=color, ) def drawBoard(app_inst: AppWrapper): for row in range(app_inst.rows): for col in range(app_inst.cols): drawCell(app_inst, row, col) def drawBoardBorder(app_inst: AppWrapper): drawRect( app_inst.boardLeft, app_inst.boardTop, app_inst.boardWidth, app_inst.boardHeight, fill=None, border="black", borderWidth=2 * app_inst.cellBorderWidth, ) def drawCell(app_inst: AppWrapper, row, col): cellLeft, cellTop = getCellLeftTop(app_inst, row, col) cellWidth, cellHeight = getCellSize(app_inst) drawRect( cellLeft, cellTop, cellWidth, cellHeight, fill=None, border="gray", borderWidth=app_inst.cellBorderWidth, ) def getCellLeftTop(app_inst: AppWrapper, row, col): cellWidth, cellHeight = getCellSize(app_inst) cellLeft = app_inst.boardLeft + col * cellWidth cellTop = app_inst.boardTop + row * cellHeight return (cellLeft, cellTop) def getCellSize(app_inst: AppWrapper): cellWidth = app_inst.boardWidth / app_inst.cols cellHeight = app_inst.boardHeight / app_inst.rows return (cellWidth, cellHeight) def main(): runApp() main()