Skip to content

Commit

Permalink
Merge pull request #8 from terminal-based-games/release/v0.1
Browse files Browse the repository at this point in the history
Release/v0.1
  • Loading branch information
mackkcooper authored Jul 29, 2019
2 parents caad91a + d7b5ba4 commit 5f35d46
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 12 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Contributing to OpenOuroboros
# Contributing to Ouroboros

Thank you for taking the time to contribute! :tada:

Expand Down
29 changes: 21 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# OpenOuroboros
# Ouroboros
> A terminal-based game inspired by Snake.
[![Build Status](https://travis-ci.com/terminal-based-games/ouroboros.svg?branch=master)](https://travis-ci.com/terminal-based-games/ouroboros)
[![HitCount](http://hits.dwyl.com/terminal-based-games/ouroboros.svg)](http://hits.dwyl.com/terminal-based-games/ouroboros)

"Snake" is a game that was preloaded on Nokia phones in 1997. A single player controls a square, which continually grows as it moves, resembling a snake. The objective of the game is to get the snake to eat items (by running into food objects) without running into the border of the screen, any obstacles, or itself.

## Installation
## Build & Run

OS X & Linux:
```sh
Expand All @@ -26,12 +25,22 @@ $ python3 ouroboros.py

## Play

**Objective:** Eat as many apples as you can without running into yourself.

**Move:** Use the arrow keys on your keyboard to move the snake (that's you!) into any apples that appear. The goal is to eat as many as you can without running into yourself. With each apple, the snake grows a bit in length.

**Pause:** Hit the space bar to pause the game, and hit the space bar again to resume.

**Exit:** Press the Escape key at any time to exit the game.

## Demo
_A demo or prototype should be completed by July 9, 2019._

![Snake Demo](https://media.giphy.com/media/MdGrSYHxXItqPhXukM/giphy.gif)

## Technology

* Python 3.7
* curses

## Roadmap

Expand All @@ -55,11 +64,13 @@ Our minimum viable product is a functional terminal-based game of Snake. This wi

## Contribute

Thank you for taking the time to contribute! Please take a moment to review our [guide to contributing](/CONTRIBUTING.md) before getting started.
Interested in contributing to this project? We'd love to have you! Please take a moment to review our [guide to contributing](/CONTRIBUTING.md) before getting started.

We welcome developers of any background and skill level. :seedling:

## Testing

To run all unit tests, you can enter the following command:
Enter the following command to run unit tests:

```
pytest
Expand All @@ -69,9 +80,11 @@ pytest

## Built By

Mack Cooper | [@mackkcooper](https://github.com/mackkcooper)
* **Mack Cooper** - [@mackkcooper](https://github.com/mackkcooper)

* **Carissa Allen** - [@carissaallen](https://github.com/carissaallen)

Carissa Allen | [@carissaallen](https://github.com/carissaallen)
All contributors who participate in this project will be recognized here.

## License
Distributed under the MIT License. See [LICENSE](/LICENSE) for more information.
219 changes: 219 additions & 0 deletions ouroboros/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
#!/usr/bin/env python3

import curses
from curses import KEY_RIGHT, KEY_LEFT, KEY_UP, KEY_DOWN
from random import randint, choice


class Apple:
"""Responsible for an apple's location on the game board."""

def __init__(self):
"""Initializes the Apple class."""

def get_apple(self, snake):
"""Returns random coordinates representing an apple's placement on the game board. These coordinates will not overlap with the snake coordinates."""
apple = []
width = Board.width - 2
height = Board.height - 2
while apple == []:
apple = [randint(1, height), randint(1, width)]
# If apple coordinates are in snake's coordinates, start over
if apple in snake:
apple = []
return apple


class Snake:
"""Responsible for handling the snake moving on the game board."""

def __init__(self):
"""Initializes the Snake class."""

def get_snake(self):
"""Returns random coordinates representing a snake's placement on the game board."""
width = Board.width - 2
height = Board.height - 2
snake = [[randint(1, height), randint(1, width)]]
return snake

def move_across_edges(self, snake):
"""Enables snake to move across the edges of the game's window."""
# Snake will enter opposite side of screen if it moves across the edge
# Moves through top edge
if snake[0][0] == 0:
snake[0][0] = Board.height - 2
# Moves through left edge
if snake[0][1] == 0:
snake[0][1] = Board.width - 2
# Moves through bottom edge
if snake[0][0] == (Board.height - 1):
snake[0][0] = 1
# Moves through right edge
if snake[0][1] == (Board.width - 1):
snake[0][1] = 1
return snake

def move(self, snake, key, window):
"""Calculates the new coordinates of the snake on the game board."""
snake.insert(
0,
[
snake[0][0] + (key == KEY_DOWN and 1) + (key == KEY_UP and -1),
snake[0][1] + (key == KEY_LEFT and -1) + (key == KEY_RIGHT and 1),
],
)
return snake

def run_over_self(self, snake):
"""If snake runs over itself, the game is over."""
if snake[0] in snake[1:]:
return True
else:
return False


class Board:
"""Responsible for game board (terminal window) setup."""

def __init__(self, height, width, begin_y, begin_x):
"""Initializes the Board class."""
Board.height = height
Board.width = width
self.begin_y = begin_y
self.begin_x = begin_x

def initial_setup(self):
"""Initializes curses and window (game board) settings."""
# Set up curses
curses.initscr() # Initialize curses
curses.start_color() # Initialize color
curses.use_default_colors() # Allow default color values
# Change definition of color pair
curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK)
curses.noecho() # Turn off automatic echoing of keys to the screen
curses.cbreak() # Enable application to react to keys instantly
curses.curs_set(0) # Disable cursor

# Initialize a new window
window = curses.newwin(
self.height, self.width, self.begin_y, self.begin_x
) # Return a new window
window.keypad(1) # Keypad will be interpreted by curses
window.nodelay(1) # getch() will be non-blocking
window.border(0) # Default border
window.timeout(220) # getch() will block delay for number of milliseconds given
window.bkgd(" ", curses.color_pair(1) | curses.A_BOLD) # Set window attributes
window.addstr(window.getmaxyx()[0] - 1, 2, "Press ESC to exit")

return window

def render(self, snake, apple):
"""Render the game board in a window generated by the curses library."""
window = self.initial_setup()
# Snake is represented by an "o" character
window.addch(snake[0][0], snake[0][1], "o")
# Apple is represented by an "*" character
window.addch(apple[0], apple[1], "*", curses.color_pair(2))

return window


class Game:
"""Responsible for executing the game."""

def __init__(self):
"""Initializes the Game class."""

def pause(self, key, prev_key, window):
"""Pauses game if the space bar is pressed; resumes game if pressed again."""
if key == ord(" "):
key = -1
# loop waiting to unpause when space bar is pressed again
while key != ord(" "):
key = window.getch()
key = prev_key
return key

def game_over(self):
"""Sets terminal functionality back to original state."""
curses.nocbreak()
curses.echo()
curses.endwin()

def play(self, snake, apple, score, window):
"""Primary controller to play the game of snake."""
try:
# Initial values
key = KEY_RIGHT
game_over = False

# Key 27 = ESC
while key != 27 and game_over == False:
# Displays the user's current score
window.addstr(
0,
window.getmaxyx()[1] - 12,
" Score: " + str(score) + " ",
curses.color_pair(1),
)

prev_key = key
# getch() refreshes the screen and waits for the user to hit a key
event = window.getch()
key = key if event == -1 else event

# Pause game if space bar is pressed;
# Resume game if space bar is pressed again
self.pause(key, prev_key, window)

# If an invalid key is pressed, do nothing
if key not in [KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, 27]:
key = prev_key

# Calculate coordinates of snake
snake = Snake().move(snake, key, window)
# Enable snake to move across game board edges
Snake().move_across_edges(snake)

# Game over if snake runs over itself
game_over = Snake().run_over_self(snake)

# Snake ate apple
if snake[0] == apple:
score += 1
apple = Apple().get_apple(snake)
# Paint a "*" character at the given (y, x) coordinates to display an apple
window.addch(apple[0], apple[1], "*", curses.color_pair(2))
else:
last = snake.pop()
window.addch(last[0], last[1], " ")
# Snake ate apple, increase its length
window.addch(snake[0][0], snake[0][1], "o")

finally:
self.game_over()


def main():
# Initial game values
height = 25
width = 75
begin_y = 0
begin_x = 0
score = 0

# Initialize game board
board = Board(height, width, begin_y, begin_x)
# Set initial coordinates for snake and apple
snake = Snake().get_snake()
apple = Apple().get_apple(snake)
# Initialize window (terminal screen)
window = board.render(snake, apple)
# Play game!
Game().play(snake, apple, score, window)


if __name__ == "__main__":
main()
3 changes: 0 additions & 3 deletions ouroboros/ouroboros.py

This file was deleted.

0 comments on commit 5f35d46

Please sign in to comment.