#!/usr/bin/python # "Caterpillar" version 1 # A cute little snake/worm/caterpillar type game # By Zoe Blade, 2009-11-07 - 2009-11-08 # Released under the GNU GPL. Share and enjoy! # Import things we'll need import curses import math import random import time # The input/output class class Interface: window_size = [24, 80] arena_size = [24, 80] y_offset = 0 x_offset = 0 arena = [] direction = 'up' speed = float(5) # The default game speed is 5Hz quit_message = 'Thanks for playing!' quit = False def __init__(self, arena_y_size, arena_x_size): self.arena_size[0] = arena_y_size self.arena_size[1] = arena_x_size self.stdscr = curses.initscr() curses.noecho() curses.cbreak() self.stdscr.keypad(True) curses.start_color() try: curses.curs_set(False) except curses.error: pass self.stdscr.nodelay(True) self.window_size = self.stdscr.getmaxyx() if self.window_size[0] < self.arena_size[0] or self.window_size[1] < self.arena_size[1] * 2: print 'Sorry, your terminal isn\'t big enough.' exit() self.y_offset = int(math.floor((self.window_size[0] - self.arena_size[0]) / 2)) self.x_offset = int(math.floor((self.window_size[1] - self.arena_size[1] * 2) / 2)) # Draw a border around the arena if it will fit if self.y_offset > 0: for x in range(self.x_offset, self.x_offset + self.arena_size[1] * 2): try: self.stdscr.addch(self.y_offset - 1, x, '=') except curses.error: pass if self.y_offset + self.arena_size[0] < self.window_size[0]: for x in range(self.x_offset, self.x_offset + self.arena_size[1] * 2): try: self.stdscr.addch(self.y_offset + self.arena_size[0], x, '=') except curses.error: pass if self.x_offset > 0: for y in range(self.y_offset, self.y_offset + self.arena_size[0]): try: self.stdscr.addch(y, self.x_offset - 1, '|') except curses.error: pass if self.x_offset + self.arena_size[1] * 2 < self.window_size[1]: for y in range(self.y_offset, self.y_offset + self.arena_size[0]): try: self.stdscr.addch(y, self.x_offset + self.arena_size[1] * 2, '|') except curses.error: pass curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_BLACK) # Pitch black void curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK) # Caterpillar body curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) # Caterpillar head curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) # Food for y in range(self.arena_size[0]): self.arena.append([]) for x in range(self.arena_size[1]): self.arena[y].append([]) def getInput(self): time.sleep(1 / self.speed) input = self.stdscr.getch() if input == ord('k') or input == curses.KEY_UP or input == ord('w'): self.direction = 'up' elif input == ord('j') or input == curses.KEY_DOWN or input == ord('s'): self.direction = 'down' elif input == ord('h') or input == curses.KEY_LEFT or input == ord('a'): self.direction = 'left' elif input == ord('l') or input == curses.KEY_RIGHT or input == ord('d'): self.direction = 'right' elif input == ord('q') or input == curses.KEY_BREAK: self.quit = True def draw(self, y, x, object): self.arena[y][x] = object return True def refreshScreen(self): for y in range(self.arena_size[0]): for x in range(self.arena_size[1]): if self.arena[y][x] == 'caterpillar_head': self.stdscr.addch(self.y_offset + y, self.x_offset + x * 2, 'O', curses.color_pair(3)) try: self.stdscr.addch(self.y_offset + y, self.x_offset + x * 2 + 1, 'O', curses.color_pair(3)) except curses.error: pass # It's OK if the cursor falls off the screen... elif self.arena[y][x] == 'caterpillar_body': self.stdscr.addch(self.y_offset + y, self.x_offset + x * 2, '(', curses.color_pair(2)) try: self.stdscr.addch(self.y_offset + y, self.x_offset + x * 2 + 1, ')', curses.color_pair(2)) except curses.error: pass # It's OK if the cursor falls off the screen... elif self.arena[y][x] == 'food': self.stdscr.addch(self.y_offset + y, self.x_offset + x * 2, '@', curses.color_pair(4)) try: self.stdscr.addch(self.y_offset + y, self.x_offset + x * 2 + 1, '@', curses.color_pair(4)) except curses.error: pass # It's OK if the cursor falls off the screen... else: self.stdscr.addch(self.y_offset + y, self.x_offset + x * 2, ' ', curses.color_pair(1)) try: interface.stdscr.addch(self.y_offset + y, self.x_offset + x * 2 + 1, ' ', curses.color_pair(1)) except curses.error: pass self.stdscr.move(0, 0) # ...because we'll snap it right back to the start! self.stdscr.refresh() # The caterpillar class (the protagonist) class Caterpillar: body_parts = [] mood = 'hungry' def __init__(self, head_vertical_position, head_horizontal_position): for body_part in range(3): # Default body length self.body_parts.append([]) self.body_parts[-1].append(head_vertical_position) self.body_parts[-1].append(head_horizontal_position) def moveUp(self): head_vertical_position = self.body_parts[0][0] head_horizontal_position = self.body_parts[0][1] if head_vertical_position > 0 and interface.arena[head_vertical_position - 1][head_horizontal_position] != 'caterpillar_body': self.body_parts.insert(0, []) # Head first! self.body_parts[0].append(head_vertical_position - 1) self.body_parts[0].append(head_horizontal_position) if interface.arena[head_vertical_position - 1][head_horizontal_position] == 'food': self.mood = 'hungry' else: self.body_parts.pop() # Trim tail return 1 else: return 0 def moveDown(self): head_vertical_position = self.body_parts[0][0] head_horizontal_position = self.body_parts[0][1] if head_vertical_position < interface.arena_size[0] - 1 and interface.arena[head_vertical_position + 1][head_horizontal_position] != 'caterpillar_body': self.body_parts.insert(0, []) # Head first! self.body_parts[0].append(head_vertical_position + 1) self.body_parts[0].append(head_horizontal_position) if interface.arena[head_vertical_position + 1][head_horizontal_position] == 'food': self.mood = 'hungry' else: self.body_parts.pop() # Trim tail return 1 else: return 0 def moveLeft(self): head_vertical_position = self.body_parts[0][0] head_horizontal_position = self.body_parts[0][1] if head_horizontal_position > 0 and interface.arena[head_vertical_position][head_horizontal_position - 1] != 'caterpillar_body': self.body_parts.insert(0, []) # Head first! self.body_parts[0].append(head_vertical_position) self.body_parts[0].append(head_horizontal_position - 1) if interface.arena[head_vertical_position][head_horizontal_position - 1] == 'food': self.mood = 'hungry' else: self.body_parts.pop() # Trim tail return 1 else: return 0 def moveRight(self): head_vertical_position = self.body_parts[0][0] head_horizontal_position = self.body_parts[0][1] if head_horizontal_position < interface.arena_size[1] - 1 and interface.arena[head_vertical_position][head_horizontal_position + 1] != 'caterpillar_body': self.body_parts.insert(0, []) # Head first! self.body_parts[0].append(head_vertical_position) self.body_parts[0].append(head_horizontal_position + 1) if interface.arena[head_vertical_position][head_horizontal_position + 1] == 'food': self.mood = 'hungry' else: self.body_parts.pop() # Trim tail return 1 else: return 0 def checkIfStuck(self): head_vertical_position = self.body_parts[0][0] head_horizontal_position = self.body_parts[0][1] can_move_up = False; can_move_down = False; can_move_left = False; can_move_right = False; if head_vertical_position > 0 and interface.arena[head_vertical_position - 1][head_horizontal_position] != 'caterpillar_body': can_move_up = True; if head_vertical_position < interface.arena_size[0] - 1 and interface.arena[head_vertical_position+ 1][head_horizontal_position] != 'caterpillar_body': can_move_down = True; if head_horizontal_position > 0 and interface.arena[head_vertical_position][head_horizontal_position - 1] != 'caterpillar_body': can_move_left = True; if head_horizontal_position < interface.arena_size[1] - 1 and interface.arena[head_vertical_position][head_horizontal_position + 1] != 'caterpillar_body': can_move_right = True; if can_move_up == False and can_move_down == False and can_move_left == False and can_move_right == False: self.mood = 'stuck' # The food class class Food: meals = [] def __init__(self): for meal in range(3): # Default meals self.meals.append([]) self.meals[-1].append(random.randint(0, interface.arena_size[0] - 1)) self.meals[-1].append(random.randint(0, interface.arena_size[1] - 1)) def move(self): for meal in self.meals: meal_y_position = meal[0] meal_x_position = meal[1] nina_y_position = nina.body_parts[0][0] nina_x_position = nina.body_parts[0][1] if meal_y_position == nina_y_position and meal_x_position == nina_x_position: meal_y_position = random.randint(0, interface.arena_size[0] - 1) meal_x_position = random.randint(0, interface.arena_size[1] - 1) # Keep moving the food until it's not in the way while interface.arena[meal_y_position][meal_x_position] != 'empty': meal_y_position = random.randint(0, interface.arena_size[0] - 1) meal_x_position = random.randint(0, interface.arena_size[1] - 1) meal[0] = meal_y_position meal[1] = meal_x_position # The main program interface = Interface(16, 16) # Create the interface nina = Caterpillar(int(math.floor(interface.arena_size[0] / 2)), int(math.floor(interface.arena_size[1] / 2))) # Create our heroine food = Food() # Create some food while 1: if nina.mood == 'hungry': interface.speed = interface.speed + 1 # Increase speed by 1Hz food.move() nina.mood = 'adventurous' # Clear the screen for y in range(interface.arena_size[0]): for x in range(interface.arena_size[1]): interface.arena[y][x] = 'empty' # Draw Nina! # Draw the whole body for nina_body_part in nina.body_parts: interface.draw(nina_body_part[0], nina_body_part[1], 'caterpillar_body') # Draw the head again, this time looking like a head (keeping it on top) interface.draw(nina.body_parts[0][0], nina.body_parts[0][1], 'caterpillar_head') # Draw the food for meal in food.meals: interface.draw(meal[0], meal[1], 'food') interface.refreshScreen() interface.getInput() if interface.quit == True: # Destroy the window interface.stdscr.keypad(False) curses.nocbreak() curses.echo() curses.endwin() print interface.quit_message exit() elif interface.direction == 'up': nina.moveUp() elif interface.direction == 'down': nina.moveDown() elif interface.direction == 'left': nina.moveLeft() elif interface.direction == 'right': nina.moveRight() nina.checkIfStuck() if (nina.mood == 'stuck'): interface.quit_message = 'I\'m stuck!' interface.quit = True