├── LICENSE ├── README.md ├── ezgif.com-optimize.gif └── main.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 JonPizza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Crappy Bird! (Python Edition) 2 | 3 | ![flappy bird gif](https://raw.githubusercontent.com/JonPizza/crappybird-py/master/ezgif.com-optimize.gif) 4 | 5 | It's flappy bird, but it's crappy... 6 | 7 | Uses the curses module. To run, make sure your terminal is 80x24. 8 | 9 | Insipred by https://github.com/say4n/crappybird (C++) 10 | -------------------------------------------------------------------------------- /ezgif.com-optimize.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonPizza/crappybird-py/12947a0c864da986d2de0c3a69ffed28a2b9cc30/ezgif.com-optimize.gif -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import curses 2 | from curses import wrapper 3 | import time 4 | import random 5 | 6 | 7 | class Birdie: 8 | def __init__(self, y=5, downward_accl=0): 9 | self.y = y 10 | self.downward_accl = downward_accl # downward acceleration 11 | 12 | def flap(self): 13 | self.y -= 1 14 | self.downward_accl = -2 15 | 16 | def update(self, counter): 17 | if counter % 2 == 0: 18 | self.y += 1 + self.downward_accl 19 | if counter % 10 == 0: 20 | self.downward_accl += 1 21 | 22 | def draw(self, stdscr): 23 | stdscr.addstr(self.y, 20, '>', curses.color_pair(2)) 24 | stdscr.refresh() 25 | 26 | 27 | class Pipe: 28 | def __init__(self, x=78, opening=None): 29 | if opening: 30 | self.opening = opening 31 | else: 32 | self.opening = random.randint(2, 13) 33 | self.x = x 34 | 35 | def update(self): 36 | self.x -= 1 37 | 38 | def draw(self, stdscr): 39 | for i in range(24): 40 | if i not in [i for i in range(self.opening, self.opening+7)]: 41 | stdscr.addstr(i, self.x, '█') 42 | stdscr.refresh() 43 | 44 | def karl_within(self, karl: Birdie): 45 | locations = [] 46 | for i in range(24): 47 | if i not in [i for i in range(self.opening, self.opening+7)]: 48 | locations.append([i, self.x]) 49 | 50 | return [karl.y, 20] in locations 51 | 52 | def karl_passing(self, karl: Birdie): 53 | locations = [] 54 | for i in range(24): 55 | if i in [i for i in range(self.opening, self.opening+7)]: 56 | locations.append([i, self.x]) 57 | 58 | return [karl.y, 20] in locations 59 | 60 | 61 | def main(stdscr): 62 | karl = Birdie() 63 | pipes = [] 64 | counter = 0 65 | points = 0 66 | 67 | curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLUE) 68 | curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_RED) 69 | curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK) 70 | curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_YELLOW) 71 | 72 | stdscr.bkgd(' ', curses.color_pair(1) | curses.A_BOLD) 73 | 74 | stdscr.nodelay(True) 75 | curses.curs_set(0) 76 | 77 | while True: 78 | if points == 35: 79 | # change to HELL MODE 80 | curses.init_pair(1, curses.COLOR_MAGENTA, curses.COLOR_BLACK) 81 | curses.init_pair(2, curses.COLOR_RED, curses.COLOR_YELLOW) 82 | curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK) 83 | 84 | stdscr.erase() 85 | 86 | k = stdscr.getch() 87 | if k == ord(' '): 88 | karl.flap() 89 | karl.update(counter) 90 | 91 | try: # throws error if karl is outside of stdscr 92 | karl.draw(stdscr) 93 | except: 94 | return points 95 | 96 | for pipe in pipes: 97 | if pipe.x >= 0: 98 | pipe.draw(stdscr) 99 | pipe.update() 100 | if pipe.karl_within(karl): 101 | return points 102 | if pipe.karl_passing(karl): 103 | points += 1 104 | else: 105 | del pipe 106 | 107 | if counter % 40 == 0: 108 | pipes.append(Pipe()) 109 | 110 | stdscr.addstr(0, 0, f'Points: {points}', curses.color_pair(3)) 111 | stdscr.addstr(23, 0, '█'*79, curses.color_pair(4)) 112 | 113 | stdscr.refresh() 114 | counter += 1 115 | time.sleep(0.04) 116 | 117 | 118 | def death_message(points): 119 | if points == 0: 120 | print('What\'s wrong with you?? Eh??') 121 | elif points < 10: 122 | print('That\'s all you got!?') 123 | elif points < 30: 124 | print('You\'re getting a little better...') 125 | elif points < 50: 126 | print('Wowza! That\'s pretty darn good!') 127 | else: 128 | print('You must be a hacker.') 129 | 130 | 131 | if __name__ == '__main__': 132 | points = wrapper(main) 133 | print(f'Points: {points}') 134 | death_message(points) 135 | 136 | # https://jon.network 137 | --------------------------------------------------------------------------------