├── LICENSE.txt ├── README.md ├── code ├── ch10_intermezzo │ ├── game.py │ ├── items.py │ └── player.py ├── ch11_world │ ├── game.py │ ├── items.py │ ├── player.py │ └── world.py ├── ch12_enemies │ ├── enemies.py │ ├── game.py │ ├── items.py │ ├── player.py │ └── world.py ├── ch13_world2 │ ├── enemies.py │ ├── game.py │ ├── items.py │ ├── player.py │ └── world.py ├── ch14_economy │ ├── enemies.py │ ├── game.py │ ├── items.py │ ├── npc.py │ ├── player.py │ └── world.py ├── ch15_endgame │ ├── enemies.py │ ├── game.py │ ├── items.py │ ├── npc.py │ ├── player.py │ └── world.py ├── ch2_first │ └── game.py ├── ch3_io │ ├── echo.py │ └── game.py ├── ch4_decisions │ └── game.py ├── ch5_functions │ ├── game.py │ ├── hello.py │ └── hello_name.py ├── ch6_lists │ └── game.py ├── ch7_loops │ ├── factors.py │ ├── favorites.py │ ├── favorites_pretty.py │ ├── favorites_pretty_counter.py │ ├── favorites_pretty_enumerate.py │ ├── favorites_pretty_range.py │ └── game.py ├── ch8_objects │ ├── census.py │ └── game.py ├── ch9_exceptions │ ├── game.py │ └── validate.py └── hw-solutions │ ├── ch2_first │ └── calculator.py │ ├── ch3_io │ └── echo.py │ ├── ch4_decisions │ └── ages.py │ ├── ch5_functions │ ├── calculator.py │ ├── doubler.py │ └── user_calculator.py │ ├── ch6_lists │ └── favorites.py │ ├── ch7_loops │ ├── greek.py │ ├── multiplication.py │ └── user_calculator.py │ ├── ch8_objects │ └── food.py │ └── ch9_exceptions │ ├── user_calculator.py │ └── vehicles.py └── contributing.md /LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/make-your-own-python-text-adventure/81db2fa22a5470f3433171ffa81361ccd7d0701e/LICENSE.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apress Source Code 2 | 3 | This repository accompanies [*Make Your Own Python Text Adventure*](http://www.apress.com/9781484232309) by Phillip Johnson (Apress, 2018). 4 | 5 | [comment]: #cover 6 | 7 | 8 | Download the files as a zip using the green button, or clone the repository to your machine using Git. 9 | 10 | ## Releases 11 | 12 | Release v1.0 corresponds to the code in the published book, without corrections or updates. 13 | 14 | ## Contributions 15 | 16 | See the file Contributing.md for more information on how you can contribute to this repository. 17 | -------------------------------------------------------------------------------- /code/ch10_intermezzo/game.py: -------------------------------------------------------------------------------- 1 | from player import Player 2 | 3 | 4 | def play(): 5 | print("Escape from Cave Terror!") 6 | player = Player() 7 | while True: 8 | action_input = get_player_command() 9 | if action_input in ['n', 'N']: 10 | print("Go North!") 11 | elif action_input in ['s', 'S']: 12 | print("Go South!") 13 | elif action_input in ['e', 'E']: 14 | print("Go East!") 15 | elif action_input in ['w', 'W']: 16 | print("Go West!") 17 | elif action_input in ['i', 'I']: 18 | player.print_inventory() 19 | else: 20 | print("Invalid action!") 21 | 22 | 23 | def get_player_command(): 24 | return input('Action: ') 25 | 26 | 27 | play() 28 | -------------------------------------------------------------------------------- /code/ch10_intermezzo/items.py: -------------------------------------------------------------------------------- 1 | class Weapon: 2 | def __init__(self): 3 | raise NotImplementedError("Do not create raw Weapon objects.") 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | 9 | class Rock(Weapon): 10 | def __init__(self): 11 | self.name = "Rock" 12 | self.description = "A fist-sized rock, suitable for bludgeoning." 13 | self.damage = 5 14 | 15 | 16 | class Dagger(Weapon): 17 | def __init__(self): 18 | self.name = "Dagger" 19 | self.description = "A small dagger with some rust. " \ 20 | "Somewhat more dangerous than a rock." 21 | self.damage = 10 22 | 23 | 24 | class RustySword(Weapon): 25 | def __init__(self): 26 | self.name = "Rusty sword" 27 | self.description = "This sword is showing its age, " \ 28 | "but still has some fight in it." 29 | self.damage = 20 30 | -------------------------------------------------------------------------------- /code/ch10_intermezzo/player.py: -------------------------------------------------------------------------------- 1 | import items 2 | 3 | 4 | class Player: 5 | def __init__(self): 6 | self.inventory = [items.Rock(), 7 | items.Dagger(), 8 | 'Gold(5)', 9 | 'Crusty Bread'] 10 | 11 | def print_inventory(self): 12 | print("Inventory:") 13 | for item in self.inventory: 14 | print('* ' + str(item)) 15 | best_weapon = self.most_powerful_weapon() 16 | print("Your best weapon is your {}".format(best_weapon)) 17 | 18 | def most_powerful_weapon(self): 19 | max_damage = 0 20 | best_weapon = None 21 | for item in self.inventory: 22 | try: 23 | if item.damage > max_damage: 24 | best_weapon = item 25 | max_damage = item.damage 26 | except AttributeError: 27 | pass 28 | 29 | return best_weapon 30 | -------------------------------------------------------------------------------- /code/ch11_world/game.py: -------------------------------------------------------------------------------- 1 | from player import Player 2 | import world 3 | 4 | 5 | def play(): 6 | print("Escape from Cave Terror!") 7 | player = Player() 8 | while True: 9 | room = world.tile_at(player.x, player.y) 10 | print(room.intro_text()) 11 | action_input = get_player_command() 12 | if action_input in ['n', 'N']: 13 | player.move_north() 14 | elif action_input in ['s', 'S']: 15 | player.move_south() 16 | elif action_input in ['e', 'E']: 17 | player.move_east() 18 | elif action_input in ['w', 'W']: 19 | player.move_west() 20 | elif action_input in ['i', 'I']: 21 | player.print_inventory() 22 | else: 23 | print("Invalid action!") 24 | 25 | 26 | def get_player_command(): 27 | return input('Action: ') 28 | 29 | 30 | play() 31 | -------------------------------------------------------------------------------- /code/ch11_world/items.py: -------------------------------------------------------------------------------- 1 | class Weapon: 2 | def __init__(self): 3 | raise NotImplementedError("Do not create raw Weapon objects.") 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | 9 | class Rock(Weapon): 10 | def __init__(self): 11 | self.name = "Rock" 12 | self.description = "A fist-sized rock, suitable for bludgeoning." 13 | self.damage = 5 14 | 15 | 16 | class Dagger(Weapon): 17 | def __init__(self): 18 | self.name = "Dagger" 19 | self.description = "A small dagger with some rust. " \ 20 | "Somewhat more dangerous than a rock." 21 | self.damage = 10 22 | 23 | 24 | class RustySword(Weapon): 25 | def __init__(self): 26 | self.name = "Rusty sword" 27 | self.description = "This sword is showing its age, " \ 28 | "but still has some fight in it." 29 | self.damage = 20 30 | -------------------------------------------------------------------------------- /code/ch11_world/player.py: -------------------------------------------------------------------------------- 1 | import items 2 | 3 | 4 | class Player: 5 | def __init__(self): 6 | self.inventory = [items.Rock(), 7 | items.Dagger(), 8 | 'Gold(5)', 9 | 'Crusty Bread'] 10 | 11 | self.x = 1 12 | self.y = 2 13 | 14 | def print_inventory(self): 15 | print("Inventory:") 16 | for item in self.inventory: 17 | print('* ' + str(item)) 18 | best_weapon = self.most_powerful_weapon() 19 | print("Your best weapon is your {}".format(best_weapon)) 20 | 21 | def most_powerful_weapon(self): 22 | max_damage = 0 23 | best_weapon = None 24 | for item in self.inventory: 25 | try: 26 | if item.damage > max_damage: 27 | best_weapon = item 28 | max_damage = item.damage 29 | except AttributeError: 30 | pass 31 | 32 | return best_weapon 33 | 34 | def move(self, dx, dy): 35 | self.x += dx 36 | self.y += dy 37 | 38 | def move_north(self): 39 | self.move(dx=0, dy=-1) 40 | 41 | def move_south(self): 42 | self.move(dx=0, dy=1) 43 | 44 | def move_east(self): 45 | self.move(dx=1, dy=0) 46 | 47 | def move_west(self): 48 | self.move(dx=-1, dy=0) 49 | -------------------------------------------------------------------------------- /code/ch11_world/world.py: -------------------------------------------------------------------------------- 1 | class MapTile: 2 | def __init__(self, x, y): 3 | self.x = x 4 | self.y = y 5 | 6 | def intro_text(self): 7 | raise NotImplementedError("Create a subclass instead!") 8 | 9 | 10 | class StartTile(MapTile): 11 | def intro_text(self): 12 | return """ 13 | You find yourself in a cave with a flickering torch on the wall. 14 | You can make out four paths, each equally as dark and foreboding. 15 | """ 16 | 17 | 18 | class BoringTile(MapTile): 19 | def intro_text(self): 20 | return """ 21 | This is a very boring part of the cave. 22 | """ 23 | 24 | 25 | class VictoryTile(MapTile): 26 | def intro_text(self): 27 | return """ 28 | You see a bright light in the distance... 29 | ... it grows as you get closer! It's sunlight! 30 | 31 | 32 | Victory is yours! 33 | """ 34 | 35 | world_map = [ 36 | [None,VictoryTile(1,0),None], 37 | [None,BoringTile(1,1),None], 38 | [BoringTile(0,2),StartTile(1,2),BoringTile(2,2)], 39 | [None,BoringTile(1,3),None] 40 | ] 41 | 42 | def tile_at(x, y): 43 | if x < 0 or y < 0: 44 | return None 45 | try: 46 | return world_map[y][x] 47 | except IndexError: 48 | return None 49 | -------------------------------------------------------------------------------- /code/ch12_enemies/enemies.py: -------------------------------------------------------------------------------- 1 | class Enemy: 2 | def __init__(self): 3 | raise NotImplementedError("Do not create raw Enemy objects.") 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | def is_alive(self): 9 | return self.hp > 0 10 | 11 | 12 | class GiantSpider(Enemy): 13 | def __init__(self): 14 | self.name = "Giant Spider" 15 | self.hp = 10 16 | self.damage = 2 17 | 18 | 19 | class Ogre(Enemy): 20 | def __init__(self): 21 | self.name = "Ogre" 22 | self.hp = 30 23 | self.damage = 10 24 | 25 | 26 | class BatColony(Enemy): 27 | def __init__(self): 28 | self.name = "Colony of bats" 29 | self.hp = 100 30 | self.damage = 4 31 | 32 | 33 | class RockMonster(Enemy): 34 | def __init__(self): 35 | self.name = "Rock Monster" 36 | self.hp = 80 37 | self.damage = 15 38 | -------------------------------------------------------------------------------- /code/ch12_enemies/game.py: -------------------------------------------------------------------------------- 1 | from player import Player 2 | import world 3 | 4 | 5 | def play(): 6 | print("Escape from Cave Terror!") 7 | player = Player() 8 | while True: 9 | room = world.tile_at(player.x, player.y) 10 | print(room.intro_text()) 11 | room.modify_player(player) # New line 12 | action_input = get_player_command() 13 | if action_input in ['n', 'N']: 14 | player.move_north() 15 | elif action_input in ['s', 'S']: 16 | player.move_south() 17 | elif action_input in ['e', 'E']: 18 | player.move_east() 19 | elif action_input in ['w', 'W']: 20 | player.move_west() 21 | elif action_input in ['i', 'I']: 22 | player.print_inventory() 23 | elif action_input in ['a', 'A']: 24 | player.attack() 25 | elif action_input in ['h', 'H']: 26 | player.heal() 27 | else: 28 | print("Invalid action!") 29 | 30 | 31 | def get_player_command(): 32 | return input('Action: ') 33 | 34 | 35 | play() 36 | -------------------------------------------------------------------------------- /code/ch12_enemies/items.py: -------------------------------------------------------------------------------- 1 | class Weapon: 2 | def __init__(self): 3 | raise NotImplementedError("Do not create raw Weapon objects.") 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | 9 | class Rock(Weapon): 10 | def __init__(self): 11 | self.name = "Rock" 12 | self.description = "A fist-sized rock, suitable for bludgeoning." 13 | self.damage = 5 14 | 15 | 16 | class Dagger(Weapon): 17 | def __init__(self): 18 | self.name = "Dagger" 19 | self.description = "A small dagger with some rust. " \ 20 | "Somewhat more dangerous than a rock." 21 | self.damage = 10 22 | 23 | 24 | class RustySword(Weapon): 25 | def __init__(self): 26 | self.name = "Rusty sword" 27 | self.description = "This sword is showing its age, " \ 28 | "but still has some fight in it." 29 | self.damage = 20 30 | 31 | 32 | class Consumable: 33 | def __init__(self): 34 | raise NotImplementedError("Do not create raw Consumable objects.") 35 | 36 | def __str__(self): 37 | return "{} (+{} HP)".format(self.name, self.healing_value) 38 | 39 | 40 | class CrustyBread(Consumable): 41 | def __init__(self): 42 | self.name = "Crusty Bread" 43 | self.healing_value = 10 44 | -------------------------------------------------------------------------------- /code/ch12_enemies/player.py: -------------------------------------------------------------------------------- 1 | import items 2 | import world 3 | 4 | class Player: 5 | def __init__(self): 6 | self.inventory = [items.Rock(), 7 | items.Dagger(), 8 | 'Gold(5)', 9 | items.CrustyBread()] 10 | self.x = 1 11 | self.y = 2 12 | self.hp = 100 13 | 14 | def print_inventory(self): 15 | print("Inventory:") 16 | for item in self.inventory: 17 | print('* ' + str(item)) 18 | 19 | def heal(self): 20 | consumables = [item for item in self.inventory 21 | if isinstance(item, items.Consumable)] 22 | if not consumables: 23 | print("You don't have any items to heal you!") 24 | return 25 | 26 | for i, item in enumerate(consumables, 1): 27 | print("Choose an item to use to heal: ") 28 | print("{}. {}".format(i, item)) 29 | 30 | valid = False 31 | while not valid: 32 | choice = input("") 33 | try: 34 | to_eat = consumables[int(choice) - 1] 35 | self.hp = min(100, self.hp + to_eat.healing_value) 36 | self.inventory.remove(to_eat) 37 | print("Current HP: {}".format(self.hp)) 38 | valid = True 39 | except (ValueError, IndexError): 40 | print("Invalid choice, try again.") 41 | 42 | def most_powerful_weapon(self): 43 | max_damage = 0 44 | best_weapon = None 45 | for item in self.inventory: 46 | try: 47 | if item.damage > max_damage: 48 | best_weapon = item 49 | max_damage = item.damage 50 | except AttributeError: 51 | pass 52 | 53 | return best_weapon 54 | 55 | def move(self, dx, dy): 56 | self.x += dx 57 | self.y += dy 58 | 59 | def move_north(self): 60 | self.move(dx=0, dy=-1) 61 | 62 | def move_south(self): 63 | self.move(dx=0, dy=1) 64 | 65 | def move_east(self): 66 | self.move(dx=1, dy=0) 67 | 68 | def move_west(self): 69 | self.move(dx=-1, dy=0) 70 | 71 | def attack(self): 72 | best_weapon = self.most_powerful_weapon() 73 | room = world.tile_at(self.x, self.y) 74 | enemy = room.enemy 75 | print("You use {} against {}!".format(best_weapon.name, enemy.name)) 76 | enemy.hp -= best_weapon.damage 77 | if not enemy.is_alive(): 78 | print("You killed {}!".format(enemy.name)) 79 | else: 80 | print("{} HP is {}.".format(enemy.name, enemy.hp)) 81 | -------------------------------------------------------------------------------- /code/ch12_enemies/world.py: -------------------------------------------------------------------------------- 1 | import enemies 2 | import random 3 | 4 | 5 | class MapTile: 6 | def __init__(self, x, y): 7 | self.x = x 8 | self.y = y 9 | 10 | def intro_text(self): 11 | raise NotImplementedError("Create a subclass instead!") 12 | 13 | def modify_player(self, player): 14 | pass 15 | 16 | 17 | class StartTile(MapTile): 18 | def intro_text(self): 19 | return """ 20 | You find yourself in a cave with a flickering torch on the wall. 21 | You can make out four paths, each equally as dark and foreboding. 22 | """ 23 | 24 | 25 | class EnemyTile(MapTile): 26 | def __init__(self, x, y): 27 | r = random.random() 28 | if r < 0.50: 29 | self.enemy = enemies.GiantSpider() 30 | self.alive_text = "A giant spider jumps down from " \ 31 | "its web in front of you!" 32 | self.dead_text = "The corpse of a dead spider " \ 33 | "rots on the ground." 34 | elif r < 0.80: 35 | self.enemy = enemies.Ogre() 36 | self.alive_text = "An ogre is blocking your path!" 37 | self.dead_text = "A dead ogre reminds you of your triumph." 38 | elif r < 0.95: 39 | self.enemy = enemies.BatColony() 40 | self.alive_text = "You hear a squeaking noise growing louder" \ 41 | "...suddenly you are lost in s swarm of bats!" 42 | self.dead_text = "Dozens of dead bats are scattered on the ground." 43 | else: 44 | self.enemy = enemies.RockMonster() 45 | self.alive_text = "You've disturbed a rock monster " \ 46 | "from his slumber!" 47 | self.dead_text = "Defeated, the monster has reverted " \ 48 | "into an ordinary rock." 49 | 50 | super().__init__(x, y) 51 | 52 | def intro_text(self): 53 | text = self.alive_text if self.enemy.is_alive() else self.dead_text 54 | return text 55 | 56 | def modify_player(self, player): 57 | if self.enemy.is_alive(): 58 | player.hp = player.hp - self.enemy.damage 59 | print("Enemy does {} damage. You have {} HP remaining.". 60 | format(self.enemy.damage, player.hp)) 61 | 62 | 63 | class VictoryTile(MapTile): 64 | def intro_text(self): 65 | return """ 66 | You see a bright light in the distance... 67 | ... it grows as you get closer! It's sunlight! 68 | 69 | 70 | Victory is yours! 71 | """ 72 | 73 | world_map = [ 74 | [None,VictoryTile(1,0),None], 75 | [None,EnemyTile(1,1),None], 76 | [EnemyTile(0,2),StartTile(1,2),EnemyTile(2,2)], 77 | [None,EnemyTile(1,3),None] 78 | ] 79 | 80 | def tile_at(x, y): 81 | if x < 0 or y < 0: 82 | return None 83 | try: 84 | return world_map[y][x] 85 | except IndexError: 86 | return None 87 | -------------------------------------------------------------------------------- /code/ch13_world2/enemies.py: -------------------------------------------------------------------------------- 1 | class Enemy: 2 | def __init__(self): 3 | raise NotImplementedError("Do not create raw Enemy objects.") 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | def is_alive(self): 9 | return self.hp > 0 10 | 11 | 12 | class GiantSpider(Enemy): 13 | def __init__(self): 14 | self.name = "Giant Spider" 15 | self.hp = 10 16 | self.damage = 2 17 | 18 | 19 | class Ogre(Enemy): 20 | def __init__(self): 21 | self.name = "Ogre" 22 | self.hp = 30 23 | self.damage = 10 24 | 25 | 26 | class BatColony(Enemy): 27 | def __init__(self): 28 | self.name = "Colony of bats" 29 | self.hp = 100 30 | self.damage = 4 31 | 32 | 33 | class RockMonster(Enemy): 34 | def __init__(self): 35 | self.name = "Rock Monster" 36 | self.hp = 80 37 | self.damage = 15 38 | -------------------------------------------------------------------------------- /code/ch13_world2/game.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | from player import Player 3 | import world 4 | 5 | 6 | def play(): 7 | print("Escape from Cave Terror!") 8 | world.parse_world_dsl() 9 | player = Player() 10 | while True: 11 | room = world.tile_at(player.x, player.y) 12 | print(room.intro_text()) 13 | room.modify_player(player) 14 | choose_action(room, player) 15 | 16 | 17 | def choose_action(room, player): 18 | action = None 19 | while not action: 20 | available_actions = get_available_actions(room, player) 21 | action_input = input("Action: ") 22 | action = available_actions.get(action_input) 23 | if action: 24 | action() 25 | else: 26 | print("Invalid action!") 27 | 28 | 29 | def get_available_actions(room, player): 30 | actions = OrderedDict() 31 | print("Choose an action: ") 32 | if player.inventory: 33 | action_adder(actions, 'i', player.print_inventory, "Print inventory") 34 | if isinstance(room, world.EnemyTile) and room.enemy.is_alive(): 35 | action_adder(actions, 'a', player.attack, "Attack") 36 | else: 37 | if world.tile_at(room.x, room.y - 1): 38 | action_adder(actions, 'n', player.move_north, "Go north") 39 | if world.tile_at(room.x, room.y + 1): 40 | action_adder(actions, 's', player.move_south, "Go south") 41 | if world.tile_at(room.x + 1, room.y): 42 | action_adder(actions, 'e', player.move_east, "Go east") 43 | if world.tile_at(room.x - 1, room.y): 44 | action_adder(actions, 'w', player.move_west, "Go west") 45 | if player.hp < 100: 46 | action_adder(actions, 'h', player.heal, "Heal") 47 | 48 | return actions 49 | 50 | def action_adder(action_dict, hotkey, action, name): 51 | action_dict[hotkey.lower()] = action 52 | action_dict[hotkey.upper()] = action 53 | print("{}: {}".format(hotkey, name)) 54 | 55 | 56 | play() 57 | -------------------------------------------------------------------------------- /code/ch13_world2/items.py: -------------------------------------------------------------------------------- 1 | class Weapon: 2 | def __init__(self): 3 | raise NotImplementedError("Do not create raw Weapon objects.") 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | 9 | class Rock(Weapon): 10 | def __init__(self): 11 | self.name = "Rock" 12 | self.description = "A fist-sized rock, suitable for bludgeoning." 13 | self.damage = 5 14 | 15 | 16 | class Dagger(Weapon): 17 | def __init__(self): 18 | self.name = "Dagger" 19 | self.description = "A small dagger with some rust. " \ 20 | "Somewhat more dangerous than a rock." 21 | self.damage = 10 22 | 23 | 24 | class RustySword(Weapon): 25 | def __init__(self): 26 | self.name = "Rusty sword" 27 | self.description = "This sword is showing its age, " \ 28 | "but still has some fight in it." 29 | self.damage = 20 30 | 31 | 32 | class Consumable: 33 | def __init__(self): 34 | raise NotImplementedError("Do not create raw Consumable objects.") 35 | 36 | def __str__(self): 37 | return "{} (+{} HP)".format(self.name, self.healing_value) 38 | 39 | 40 | class CrustyBread(Consumable): 41 | def __init__(self): 42 | self.name = "Crusty Bread" 43 | self.healing_value = 10 44 | -------------------------------------------------------------------------------- /code/ch13_world2/player.py: -------------------------------------------------------------------------------- 1 | import items 2 | import world 3 | 4 | class Player: 5 | def __init__(self): 6 | self.inventory = [items.Rock(), 7 | items.Dagger(), 8 | 'Gold(5)', 9 | items.CrustyBread()] 10 | self.x = 1 11 | self.y = 2 12 | self.hp = 100 13 | 14 | def print_inventory(self): 15 | print("Inventory:") 16 | for item in self.inventory: 17 | print('* ' + str(item)) 18 | 19 | def heal(self): 20 | consumables = [item for item in self.inventory 21 | if isinstance(item, items.Consumable)] 22 | if not consumables: 23 | print("You don't have any items to heal you!") 24 | return 25 | 26 | for i, item in enumerate(consumables, 1): 27 | print("Choose an item to use to heal: ") 28 | print("{}. {}".format(i, item)) 29 | 30 | valid = False 31 | while not valid: 32 | choice = input("") 33 | try: 34 | to_eat = consumables[int(choice) - 1] 35 | self.hp = min(100, self.hp + to_eat.healing_value) 36 | self.inventory.remove(to_eat) 37 | print("Current HP: {}".format(self.hp)) 38 | valid = True 39 | except (ValueError, IndexError): 40 | print("Invalid choice, try again.") 41 | 42 | def most_powerful_weapon(self): 43 | max_damage = 0 44 | best_weapon = None 45 | for item in self.inventory: 46 | try: 47 | if item.damage > max_damage: 48 | best_weapon = item 49 | max_damage = item.damage 50 | except AttributeError: 51 | pass 52 | 53 | return best_weapon 54 | 55 | def move(self, dx, dy): 56 | self.x += dx 57 | self.y += dy 58 | 59 | def move_north(self): 60 | self.move(dx=0, dy=-1) 61 | 62 | def move_south(self): 63 | self.move(dx=0, dy=1) 64 | 65 | def move_east(self): 66 | self.move(dx=1, dy=0) 67 | 68 | def move_west(self): 69 | self.move(dx=-1, dy=0) 70 | 71 | def attack(self): 72 | best_weapon = self.most_powerful_weapon() 73 | room = world.tile_at(self.x, self.y) 74 | enemy = room.enemy 75 | print("You use {} against {}!".format(best_weapon.name, enemy.name)) 76 | enemy.hp -= best_weapon.damage 77 | if not enemy.is_alive(): 78 | print("You killed {}!".format(enemy.name)) 79 | else: 80 | print("{} HP is {}.".format(enemy.name, enemy.hp)) 81 | -------------------------------------------------------------------------------- /code/ch13_world2/world.py: -------------------------------------------------------------------------------- 1 | import enemies 2 | import random 3 | 4 | 5 | class MapTile: 6 | def __init__(self, x, y): 7 | self.x = x 8 | self.y = y 9 | 10 | def intro_text(self): 11 | raise NotImplementedError("Create a subclass instead!") 12 | 13 | def modify_player(self, player): 14 | pass 15 | 16 | 17 | class StartTile(MapTile): 18 | def intro_text(self): 19 | return """ 20 | You find yourself in a cave with a flickering torch on the wall. 21 | You can make out four paths, each equally as dark and foreboding. 22 | """ 23 | 24 | 25 | class EnemyTile(MapTile): 26 | def __init__(self, x, y): 27 | r = random.random() 28 | if r < 0.50: 29 | self.enemy = enemies.GiantSpider() 30 | self.alive_text = "A giant spider jumps down from " \ 31 | "its web in front of you!" 32 | self.dead_text = "The corpse of a dead spider " \ 33 | "rots on the ground." 34 | elif r < 0.80: 35 | self.enemy = enemies.Ogre() 36 | self.alive_text = "An ogre is blocking your path!" 37 | self.dead_text = "A dead ogre reminds you of your triumph." 38 | elif r < 0.95: 39 | self.enemy = enemies.BatColony() 40 | self.alive_text = "You hear a squeaking noise growing louder" \ 41 | "...suddenly you are lost in s swarm of bats!" 42 | self.dead_text = "Dozens of dead bats are scattered on the ground." 43 | else: 44 | self.enemy = enemies.RockMonster() 45 | self.alive_text = "You've disturbed a rock monster " \ 46 | "from his slumber!" 47 | self.dead_text = "Defeated, the monster has reverted " \ 48 | "into an ordinary rock." 49 | 50 | super().__init__(x, y) 51 | 52 | def intro_text(self): 53 | text = self.alive_text if self.enemy.is_alive() else self.dead_text 54 | return text 55 | 56 | def modify_player(self, player): 57 | if self.enemy.is_alive(): 58 | player.hp = player.hp - self.enemy.damage 59 | print("Enemy does {} damage. You have {} HP remaining.". 60 | format(self.enemy.damage, player.hp)) 61 | 62 | 63 | class VictoryTile(MapTile): 64 | def intro_text(self): 65 | return """ 66 | You see a bright light in the distance... 67 | ... it grows as you get closer! It's sunlight! 68 | 69 | 70 | Victory is yours! 71 | """ 72 | 73 | world_dsl = """ 74 | | |VT| | 75 | | |EN| | 76 | |EN|ST|EN| 77 | | |EN| | 78 | """ 79 | 80 | 81 | def is_dsl_valid(dsl): 82 | if dsl.count("|ST|") != 1: 83 | return False 84 | if dsl.count("|VT|") == 0: 85 | return False 86 | lines = dsl.splitlines() 87 | lines = [l for l in lines if l] 88 | pipe_counts = [line.count("|") for line in lines] 89 | for count in pipe_counts: 90 | if count != pipe_counts[0]: 91 | return False 92 | 93 | return True 94 | 95 | tile_type_dict = {"VT": VictoryTile, 96 | "EN": EnemyTile, 97 | "ST": StartTile, 98 | " ": None} 99 | 100 | 101 | world_map = [] 102 | 103 | 104 | def parse_world_dsl(): 105 | if not is_dsl_valid(world_dsl): 106 | raise SyntaxError("DSL is invalid!") 107 | 108 | dsl_lines = world_dsl.splitlines() 109 | dsl_lines = [x for x in dsl_lines if x] 110 | 111 | for y, dsl_row in enumerate(dsl_lines): 112 | row = [] 113 | dsl_cells = dsl_row.split("|") 114 | dsl_cells = [c for c in dsl_cells if c] 115 | for x, dsl_cell in enumerate(dsl_cells): 116 | tile_type = tile_type_dict[dsl_cell] 117 | row.append(tile_type(x, y) if tile_type else None) 118 | 119 | world_map.append(row) 120 | 121 | 122 | def tile_at(x, y): 123 | if x < 0 or y < 0: 124 | return None 125 | try: 126 | return world_map[y][x] 127 | except IndexError: 128 | return None 129 | -------------------------------------------------------------------------------- /code/ch14_economy/enemies.py: -------------------------------------------------------------------------------- 1 | class Enemy: 2 | def __init__(self): 3 | raise NotImplementedError("Do not create raw Enemy objects.") 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | def is_alive(self): 9 | return self.hp > 0 10 | 11 | 12 | class GiantSpider(Enemy): 13 | def __init__(self): 14 | self.name = "Giant Spider" 15 | self.hp = 10 16 | self.damage = 2 17 | 18 | 19 | class Ogre(Enemy): 20 | def __init__(self): 21 | self.name = "Ogre" 22 | self.hp = 30 23 | self.damage = 10 24 | 25 | 26 | class BatColony(Enemy): 27 | def __init__(self): 28 | self.name = "Colony of bats" 29 | self.hp = 100 30 | self.damage = 4 31 | 32 | 33 | class RockMonster(Enemy): 34 | def __init__(self): 35 | self.name = "Rock Monster" 36 | self.hp = 80 37 | self.damage = 15 38 | -------------------------------------------------------------------------------- /code/ch14_economy/game.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | from player import Player 3 | import world 4 | 5 | 6 | def play(): 7 | print("Escape from Cave Terror!") 8 | world.parse_world_dsl() 9 | player = Player() 10 | while True: 11 | room = world.tile_at(player.x, player.y) 12 | print(room.intro_text()) 13 | room.modify_player(player) 14 | choose_action(room, player) 15 | 16 | 17 | def choose_action(room, player): 18 | action = None 19 | while not action: 20 | available_actions = get_available_actions(room, player) 21 | action_input = input("Action: ") 22 | action = available_actions.get(action_input) 23 | if action: 24 | action() 25 | else: 26 | print("Invalid action!") 27 | 28 | 29 | def get_available_actions(room, player): 30 | actions = OrderedDict() 31 | print("Choose an action: ") 32 | if player.inventory: 33 | action_adder(actions, 'i', player.print_inventory, "Print inventory") 34 | if isinstance(room, world.TraderTile): 35 | action_adder(actions, 't', player.trade, "Trade") 36 | if isinstance(room, world.EnemyTile) and room.enemy.is_alive(): 37 | action_adder(actions, 'a', player.attack, "Attack") 38 | else: 39 | if world.tile_at(room.x, room.y - 1): 40 | action_adder(actions, 'n', player.move_north, "Go north") 41 | if world.tile_at(room.x, room.y + 1): 42 | action_adder(actions, 's', player.move_south, "Go south") 43 | if world.tile_at(room.x + 1, room.y): 44 | action_adder(actions, 'e', player.move_east, "Go east") 45 | if world.tile_at(room.x - 1, room.y): 46 | action_adder(actions, 'w', player.move_west, "Go west") 47 | if player.hp < 100: 48 | action_adder(actions, 'h', player.heal, "Heal") 49 | 50 | return actions 51 | 52 | def action_adder(action_dict, hotkey, action, name): 53 | action_dict[hotkey.lower()] = action 54 | action_dict[hotkey.upper()] = action 55 | print("{}: {}".format(hotkey, name)) 56 | 57 | 58 | play() 59 | -------------------------------------------------------------------------------- /code/ch14_economy/items.py: -------------------------------------------------------------------------------- 1 | class Weapon: 2 | def __init__(self): 3 | raise NotImplementedError("Do not create raw Weapon objects.") 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | 9 | class Rock(Weapon): 10 | def __init__(self): 11 | self.name = "Rock" 12 | self.description = "A fist-sized rock, suitable for bludgeoning." 13 | self.damage = 5 14 | self.value = 1 15 | 16 | 17 | class Dagger(Weapon): 18 | def __init__(self): 19 | self.name = "Dagger" 20 | self.description = "A small dagger with some rust. " \ 21 | "Somewhat more dangerous than a rock." 22 | self.damage = 10 23 | self.value = 20 24 | 25 | 26 | class RustySword(Weapon): 27 | def __init__(self): 28 | self.name = "Rusty sword" 29 | self.description = "This sword is showing its age, " \ 30 | "but still has some fight in it." 31 | self.damage = 20 32 | self.value = 100 33 | 34 | 35 | class Consumable: 36 | def __init__(self): 37 | raise NotImplementedError("Do not create raw Consumable objects.") 38 | 39 | def __str__(self): 40 | return "{} (+{} HP)".format(self.name, self.healing_value) 41 | 42 | 43 | class CrustyBread(Consumable): 44 | def __init__(self): 45 | self.name = "Crusty Bread" 46 | self.healing_value = 10 47 | self.value = 12 48 | 49 | class HealingPotion(Consumable): 50 | def __init__(self): 51 | self.name = "Healing Potion" 52 | self.healing_value = 50 53 | self.value = 60 54 | -------------------------------------------------------------------------------- /code/ch14_economy/npc.py: -------------------------------------------------------------------------------- 1 | import items 2 | 3 | 4 | class NonPlayableCharacter(): 5 | def __init__(self): 6 | raise NotImplementedError("Do not create raw NPC objects.") 7 | 8 | def __str__(self): 9 | return self.name 10 | 11 | 12 | class Trader(NonPlayableCharacter): 13 | def __init__(self): 14 | self.name = "Trader" 15 | self.gold = 100 16 | self.inventory = [items.CrustyBread(), 17 | items.CrustyBread(), 18 | items.CrustyBread(), 19 | items.HealingPotion(), 20 | items.HealingPotion()] -------------------------------------------------------------------------------- /code/ch14_economy/player.py: -------------------------------------------------------------------------------- 1 | import items 2 | import world 3 | 4 | class Player: 5 | def __init__(self): 6 | self.inventory = [items.Rock(), 7 | items.Dagger(), 8 | items.CrustyBread()] 9 | self.x = world.start_tile_location[0] 10 | self.y = world.start_tile_location[1] 11 | self.hp = 100 12 | self.gold = 5 13 | 14 | def print_inventory(self): 15 | print("Inventory:") 16 | for item in self.inventory: 17 | print('* ' + str(item)) 18 | print("Gold: {}".format(self.gold)) 19 | 20 | def heal(self): 21 | consumables = [item for item in self.inventory 22 | if isinstance(item, items.Consumable)] 23 | if not consumables: 24 | print("You don't have any items to heal you!") 25 | return 26 | 27 | for i, item in enumerate(consumables, 1): 28 | print("Choose an item to use to heal: ") 29 | print("{}. {}".format(i, item)) 30 | 31 | valid = False 32 | while not valid: 33 | choice = input("") 34 | try: 35 | to_eat = consumables[int(choice) - 1] 36 | self.hp = min(100, self.hp + to_eat.healing_value) 37 | self.inventory.remove(to_eat) 38 | print("Current HP: {}".format(self.hp)) 39 | valid = True 40 | except (ValueError, IndexError): 41 | print("Invalid choice, try again.") 42 | 43 | def most_powerful_weapon(self): 44 | max_damage = 0 45 | best_weapon = None 46 | for item in self.inventory: 47 | try: 48 | if item.damage > max_damage: 49 | best_weapon = item 50 | max_damage = item.damage 51 | except AttributeError: 52 | pass 53 | 54 | return best_weapon 55 | 56 | def move(self, dx, dy): 57 | self.x += dx 58 | self.y += dy 59 | 60 | def move_north(self): 61 | self.move(dx=0, dy=-1) 62 | 63 | def move_south(self): 64 | self.move(dx=0, dy=1) 65 | 66 | def move_east(self): 67 | self.move(dx=1, dy=0) 68 | 69 | def move_west(self): 70 | self.move(dx=-1, dy=0) 71 | 72 | def attack(self): 73 | best_weapon = self.most_powerful_weapon() 74 | room = world.tile_at(self.x, self.y) 75 | enemy = room.enemy 76 | print("You use {} against {}!".format(best_weapon.name, enemy.name)) 77 | enemy.hp -= best_weapon.damage 78 | if not enemy.is_alive(): 79 | print("You killed {}!".format(enemy.name)) 80 | else: 81 | print("{} HP is {}.".format(enemy.name, enemy.hp)) 82 | 83 | def trade(self): 84 | room = world.tile_at(self.x, self.y) 85 | room.check_if_trade(self) 86 | -------------------------------------------------------------------------------- /code/ch14_economy/world.py: -------------------------------------------------------------------------------- 1 | import enemies 2 | import npc 3 | import random 4 | 5 | 6 | class MapTile: 7 | def __init__(self, x, y): 8 | self.x = x 9 | self.y = y 10 | 11 | def intro_text(self): 12 | raise NotImplementedError("Create a subclass instead!") 13 | 14 | def modify_player(self, player): 15 | pass 16 | 17 | 18 | class StartTile(MapTile): 19 | def intro_text(self): 20 | return """ 21 | You find yourself in a cave with a flickering torch on the wall. 22 | You can make out four paths, each equally as dark and foreboding. 23 | """ 24 | 25 | 26 | class EnemyTile(MapTile): 27 | def __init__(self, x, y): 28 | r = random.random() 29 | if r < 0.50: 30 | self.enemy = enemies.GiantSpider() 31 | self.alive_text = "A giant spider jumps down from " \ 32 | "its web in front of you!" 33 | self.dead_text = "The corpse of a dead spider " \ 34 | "rots on the ground." 35 | elif r < 0.80: 36 | self.enemy = enemies.Ogre() 37 | self.alive_text = "An ogre is blocking your path!" 38 | self.dead_text = "A dead ogre reminds you of your triumph." 39 | elif r < 0.95: 40 | self.enemy = enemies.BatColony() 41 | self.alive_text = "You hear a squeaking noise growing louder" \ 42 | "...suddenly you are lost in s swarm of bats!" 43 | self.dead_text = "Dozens of dead bats are scattered on the ground." 44 | else: 45 | self.enemy = enemies.RockMonster() 46 | self.alive_text = "You've disturbed a rock monster " \ 47 | "from his slumber!" 48 | self.dead_text = "Defeated, the monster has reverted " \ 49 | "into an ordinary rock." 50 | 51 | super().__init__(x, y) 52 | 53 | def intro_text(self): 54 | text = self.alive_text if self.enemy.is_alive() else self.dead_text 55 | return text 56 | 57 | def modify_player(self, player): 58 | if self.enemy.is_alive(): 59 | player.hp = player.hp - self.enemy.damage 60 | print("Enemy does {} damage. You have {} HP remaining.". 61 | format(self.enemy.damage, player.hp)) 62 | 63 | 64 | class VictoryTile(MapTile): 65 | def intro_text(self): 66 | return """ 67 | You see a bright light in the distance... 68 | ... it grows as you get closer! It's sunlight! 69 | 70 | 71 | Victory is yours! 72 | """ 73 | 74 | 75 | class FindGoldTile(MapTile): 76 | def __init__(self, x, y): 77 | self.gold = random.randint(1, 50) 78 | self.gold_claimed = False 79 | super().__init__(x, y) 80 | 81 | def modify_player(self, player): 82 | if not self.gold_claimed: 83 | self.gold_claimed = True 84 | player.gold = player.gold + self.gold 85 | print("+{} gold added.".format(self.gold)) 86 | 87 | def intro_text(self): 88 | if self.gold_claimed: 89 | return """ 90 | Another unremarkable part of the cave. You must forge onwards. 91 | """ 92 | else: 93 | return """ 94 | Someone dropped some gold. You pick it up. 95 | """ 96 | 97 | 98 | class TraderTile(MapTile): 99 | def __init__(self, x, y): 100 | self.trader = npc.Trader() 101 | super().__init__(x, y) 102 | 103 | def check_if_trade(self, player): 104 | while True: 105 | print("Would you like to (B)uy, (S)ell, or (Q)uit?") 106 | user_input = input() 107 | if user_input in ['Q', 'q']: 108 | return 109 | elif user_input in ['B', 'b']: 110 | print("Here's whats available to buy: ") 111 | self.trade(buyer=player, seller=self.trader) 112 | elif user_input in ['S', 's']: 113 | print("Here's whats available to sell: ") 114 | self.trade(buyer=self.trader, seller=player) 115 | else: 116 | print("Invalid choice!") 117 | 118 | def trade(self, buyer, seller): 119 | for i, item in enumerate(seller.inventory, 1): 120 | print("{}. {} - {} Gold".format(i, item.name, item.value)) 121 | while True: 122 | user_input = input("Choose an item or press Q to exit: ") 123 | if user_input in ['Q', 'q']: 124 | return 125 | else: 126 | try: 127 | choice = int(user_input) 128 | to_swap = seller.inventory[choice - 1] 129 | self.swap(seller, buyer, to_swap) 130 | except ValueError: 131 | print("Invalid choice!") 132 | 133 | def swap(self, seller, buyer, item): 134 | if item.value > buyer.gold: 135 | print("That's too expensive") 136 | return 137 | seller.inventory.remove(item) 138 | buyer.inventory.append(item) 139 | seller.gold = seller.gold + item.value 140 | buyer.gold = buyer.gold - item.value 141 | print("Trade complete!") 142 | 143 | 144 | def intro_text(self): 145 | return """ 146 | A frail not-quite-human, not-quite-creature squats in the corner 147 | clinking his gold coins together. He looks willing to trade. 148 | """ 149 | 150 | world_dsl = """ 151 | |EN|EN|VT|EN|EN| 152 | |EN| | | |EN| 153 | |EN|FG|EN| |TT| 154 | |TT| |ST|FG|EN| 155 | |FG| |EN| |FG| 156 | """ 157 | 158 | 159 | def is_dsl_valid(dsl): 160 | if dsl.count("|ST|") != 1: 161 | return False 162 | if dsl.count("|VT|") == 0: 163 | return False 164 | lines = dsl.splitlines() 165 | lines = [l for l in lines if l] 166 | pipe_counts = [line.count("|") for line in lines] 167 | for count in pipe_counts: 168 | if count != pipe_counts[0]: 169 | return False 170 | 171 | return True 172 | 173 | tile_type_dict = {"VT": VictoryTile, 174 | "EN": EnemyTile, 175 | "ST": StartTile, 176 | "FG": FindGoldTile, 177 | "TT": TraderTile, 178 | " ": None} 179 | 180 | 181 | world_map = [] 182 | 183 | start_tile_location = None 184 | 185 | def parse_world_dsl(): 186 | if not is_dsl_valid(world_dsl): 187 | raise SyntaxError("DSL is invalid!") 188 | 189 | dsl_lines = world_dsl.splitlines() 190 | dsl_lines = [x for x in dsl_lines if x] 191 | 192 | for y, dsl_row in enumerate(dsl_lines): 193 | row = [] 194 | dsl_cells = dsl_row.split("|") 195 | dsl_cells = [c for c in dsl_cells if c] 196 | for x, dsl_cell in enumerate(dsl_cells): 197 | tile_type = tile_type_dict[dsl_cell] 198 | if tile_type == StartTile: 199 | global start_tile_location 200 | start_tile_location = x, y 201 | row.append(tile_type(x, y) if tile_type else None) 202 | 203 | world_map.append(row) 204 | 205 | 206 | def tile_at(x, y): 207 | if x < 0 or y < 0: 208 | return None 209 | try: 210 | return world_map[y][x] 211 | except IndexError: 212 | return None 213 | -------------------------------------------------------------------------------- /code/ch15_endgame/enemies.py: -------------------------------------------------------------------------------- 1 | class Enemy: 2 | def __init__(self): 3 | raise NotImplementedError("Do not create raw Enemy objects.") 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | def is_alive(self): 9 | return self.hp > 0 10 | 11 | 12 | class GiantSpider(Enemy): 13 | def __init__(self): 14 | self.name = "Giant Spider" 15 | self.hp = 10 16 | self.damage = 2 17 | 18 | 19 | class Ogre(Enemy): 20 | def __init__(self): 21 | self.name = "Ogre" 22 | self.hp = 30 23 | self.damage = 10 24 | 25 | 26 | class BatColony(Enemy): 27 | def __init__(self): 28 | self.name = "Colony of bats" 29 | self.hp = 100 30 | self.damage = 4 31 | 32 | 33 | class RockMonster(Enemy): 34 | def __init__(self): 35 | self.name = "Rock Monster" 36 | self.hp = 80 37 | self.damage = 15 38 | -------------------------------------------------------------------------------- /code/ch15_endgame/game.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | from player import Player 3 | import world 4 | 5 | 6 | def play(): 7 | print("Escape from Cave Terror!") 8 | world.parse_world_dsl() 9 | player = Player() 10 | while player.is_alive() and not player.victory: 11 | room = world.tile_at(player.x, player.y) 12 | print(room.intro_text()) 13 | room.modify_player(player) 14 | if player.is_alive() and not player.victory: 15 | choose_action(room, player) 16 | elif not player.is_alive(): 17 | print("Your journey has come to an early end!") 18 | 19 | 20 | def choose_action(room, player): 21 | action = None 22 | while not action: 23 | available_actions = get_available_actions(room, player) 24 | action_input = input("Action: ") 25 | action = available_actions.get(action_input) 26 | if action: 27 | action() 28 | else: 29 | print("Invalid action!") 30 | 31 | 32 | def get_available_actions(room, player): 33 | actions = OrderedDict() 34 | print("Choose an action: ") 35 | if player.inventory: 36 | action_adder(actions, 'i', player.print_inventory, "Print inventory") 37 | if isinstance(room, world.TraderTile): 38 | action_adder(actions, 't', player.trade, "Trade") 39 | if isinstance(room, world.EnemyTile) and room.enemy.is_alive(): 40 | action_adder(actions, 'a', player.attack, "Attack") 41 | else: 42 | if world.tile_at(room.x, room.y - 1): 43 | action_adder(actions, 'n', player.move_north, "Go north") 44 | if world.tile_at(room.x, room.y + 1): 45 | action_adder(actions, 's', player.move_south, "Go south") 46 | if world.tile_at(room.x + 1, room.y): 47 | action_adder(actions, 'e', player.move_east, "Go east") 48 | if world.tile_at(room.x - 1, room.y): 49 | action_adder(actions, 'w', player.move_west, "Go west") 50 | if player.hp < 100: 51 | action_adder(actions, 'h', player.heal, "Heal") 52 | 53 | return actions 54 | 55 | 56 | def action_adder(action_dict, hotkey, action, name): 57 | action_dict[hotkey.lower()] = action 58 | action_dict[hotkey.upper()] = action 59 | print("{}: {}".format(hotkey, name)) 60 | 61 | 62 | play() 63 | -------------------------------------------------------------------------------- /code/ch15_endgame/items.py: -------------------------------------------------------------------------------- 1 | class Weapon: 2 | def __init__(self): 3 | raise NotImplementedError("Do not create raw Weapon objects.") 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | 9 | class Rock(Weapon): 10 | def __init__(self): 11 | self.name = "Rock" 12 | self.description = "A fist-sized rock, suitable for bludgeoning." 13 | self.damage = 5 14 | self.value = 1 15 | 16 | 17 | class Dagger(Weapon): 18 | def __init__(self): 19 | self.name = "Dagger" 20 | self.description = "A small dagger with some rust. " \ 21 | "Somewhat more dangerous than a rock." 22 | self.damage = 10 23 | self.value = 20 24 | 25 | 26 | class RustySword(Weapon): 27 | def __init__(self): 28 | self.name = "Rusty sword" 29 | self.description = "This sword is showing its age, " \ 30 | "but still has some fight in it." 31 | self.damage = 20 32 | self.value = 100 33 | 34 | 35 | class Consumable: 36 | def __init__(self): 37 | raise NotImplementedError("Do not create raw Consumable objects.") 38 | 39 | def __str__(self): 40 | return "{} (+{} HP)".format(self.name, self.healing_value) 41 | 42 | 43 | class CrustyBread(Consumable): 44 | def __init__(self): 45 | self.name = "Crusty Bread" 46 | self.healing_value = 10 47 | self.value = 12 48 | 49 | 50 | class HealingPotion(Consumable): 51 | def __init__(self): 52 | self.name = "Healing Potion" 53 | self.healing_value = 50 54 | self.value = 60 55 | -------------------------------------------------------------------------------- /code/ch15_endgame/npc.py: -------------------------------------------------------------------------------- 1 | import items 2 | 3 | 4 | class NonPlayableCharacter(): 5 | def __init__(self): 6 | raise NotImplementedError("Do not create raw NPC objects.") 7 | 8 | def __str__(self): 9 | return self.name 10 | 11 | 12 | class Trader(NonPlayableCharacter): 13 | def __init__(self): 14 | self.name = "Trader" 15 | self.gold = 100 16 | self.inventory = [items.CrustyBread(), 17 | items.CrustyBread(), 18 | items.CrustyBread(), 19 | items.HealingPotion(), 20 | items.HealingPotion()] -------------------------------------------------------------------------------- /code/ch15_endgame/player.py: -------------------------------------------------------------------------------- 1 | import items 2 | import world 3 | 4 | 5 | class Player: 6 | def __init__(self): 7 | self.inventory = [items.Rock(), 8 | items.Dagger(), 9 | items.CrustyBread()] 10 | self.x = world.start_tile_location[0] 11 | self.y = world.start_tile_location[1] 12 | self.hp = 100 13 | self.gold = 5 14 | self.victory = False 15 | 16 | def is_alive(self): 17 | return self.hp > 0 18 | 19 | def print_inventory(self): 20 | print("Inventory:") 21 | for item in self.inventory: 22 | print('* ' + str(item)) 23 | print("Gold: {}".format(self.gold)) 24 | 25 | def heal(self): 26 | consumables = [item for item in self.inventory 27 | if isinstance(item, items.Consumable)] 28 | if not consumables: 29 | print("You don't have any items to heal you!") 30 | return 31 | 32 | for i, item in enumerate(consumables, 1): 33 | print("Choose an item to use to heal: ") 34 | print("{}. {}".format(i, item)) 35 | 36 | valid = False 37 | while not valid: 38 | choice = input("") 39 | try: 40 | to_eat = consumables[int(choice) - 1] 41 | self.hp = min(100, self.hp + to_eat.healing_value) 42 | self.inventory.remove(to_eat) 43 | print("Current HP: {}".format(self.hp)) 44 | valid = True 45 | except (ValueError, IndexError): 46 | print("Invalid choice, try again.") 47 | 48 | def most_powerful_weapon(self): 49 | max_damage = 0 50 | best_weapon = None 51 | for item in self.inventory: 52 | try: 53 | if item.damage > max_damage: 54 | best_weapon = item 55 | max_damage = item.damage 56 | except AttributeError: 57 | pass 58 | 59 | return best_weapon 60 | 61 | def move(self, dx, dy): 62 | self.x += dx 63 | self.y += dy 64 | 65 | def move_north(self): 66 | self.move(dx=0, dy=-1) 67 | 68 | def move_south(self): 69 | self.move(dx=0, dy=1) 70 | 71 | def move_east(self): 72 | self.move(dx=1, dy=0) 73 | 74 | def move_west(self): 75 | self.move(dx=-1, dy=0) 76 | 77 | def attack(self): 78 | best_weapon = self.most_powerful_weapon() 79 | room = world.tile_at(self.x, self.y) 80 | enemy = room.enemy 81 | print("You use {} against {}!".format(best_weapon.name, enemy.name)) 82 | enemy.hp -= best_weapon.damage 83 | if not enemy.is_alive(): 84 | print("You killed {}!".format(enemy.name)) 85 | else: 86 | print("{} HP is {}.".format(enemy.name, enemy.hp)) 87 | 88 | def trade(self): 89 | room = world.tile_at(self.x, self.y) 90 | room.check_if_trade(self) 91 | -------------------------------------------------------------------------------- /code/ch15_endgame/world.py: -------------------------------------------------------------------------------- 1 | import enemies 2 | import npc 3 | import random 4 | 5 | 6 | class MapTile: 7 | def __init__(self, x, y): 8 | self.x = x 9 | self.y = y 10 | 11 | def intro_text(self): 12 | raise NotImplementedError("Create a subclass instead!") 13 | 14 | def modify_player(self, player): 15 | pass 16 | 17 | 18 | class StartTile(MapTile): 19 | def intro_text(self): 20 | return """ 21 | You find yourself in a cave with a flickering torch on the wall. 22 | You can make out four paths, each equally as dark and foreboding. 23 | """ 24 | 25 | 26 | class EnemyTile(MapTile): 27 | def __init__(self, x, y): 28 | r = random.random() 29 | if r < 0.50: 30 | self.enemy = enemies.GiantSpider() 31 | self.alive_text = "A giant spider jumps down from " \ 32 | "its web in front of you!" 33 | self.dead_text = "The corpse of a dead spider " \ 34 | "rots on the ground." 35 | elif r < 0.80: 36 | self.enemy = enemies.Ogre() 37 | self.alive_text = "An ogre is blocking your path!" 38 | self.dead_text = "A dead ogre reminds you of your triumph." 39 | elif r < 0.95: 40 | self.enemy = enemies.BatColony() 41 | self.alive_text = "You hear a squeaking noise growing louder" \ 42 | "...suddenly you are lost in s swarm of bats!" 43 | self.dead_text = "Dozens of dead bats are scattered on the ground." 44 | else: 45 | self.enemy = enemies.RockMonster() 46 | self.alive_text = "You've disturbed a rock monster " \ 47 | "from his slumber!" 48 | self.dead_text = "Defeated, the monster has reverted " \ 49 | "into an ordinary rock." 50 | 51 | super().__init__(x, y) 52 | 53 | def intro_text(self): 54 | text = self.alive_text if self.enemy.is_alive() else self.dead_text 55 | return text 56 | 57 | def modify_player(self, player): 58 | if self.enemy.is_alive(): 59 | player.hp = player.hp - self.enemy.damage 60 | print("Enemy does {} damage. You have {} HP remaining.". 61 | format(self.enemy.damage, player.hp)) 62 | 63 | 64 | class VictoryTile(MapTile): 65 | def modify_player(self, player): 66 | player.victory = True 67 | 68 | def intro_text(self): 69 | return """ 70 | You see a bright light in the distance... 71 | ... it grows as you get closer! It's sunlight! 72 | 73 | 74 | Victory is yours! 75 | """ 76 | 77 | 78 | class FindGoldTile(MapTile): 79 | def __init__(self, x, y): 80 | self.gold = random.randint(1, 50) 81 | self.gold_claimed = False 82 | super().__init__(x, y) 83 | 84 | def modify_player(self, player): 85 | if not self.gold_claimed: 86 | self.gold_claimed = True 87 | player.gold = player.gold + self.gold 88 | print("+{} gold added.".format(self.gold)) 89 | 90 | def intro_text(self): 91 | if self.gold_claimed: 92 | return """ 93 | Another unremarkable part of the cave. You must forge onwards. 94 | """ 95 | else: 96 | return """ 97 | Someone dropped some gold. You pick it up. 98 | """ 99 | 100 | 101 | class TraderTile(MapTile): 102 | def __init__(self, x, y): 103 | self.trader = npc.Trader() 104 | super().__init__(x, y) 105 | 106 | def check_if_trade(self, player): 107 | while True: 108 | print("Would you like to (B)uy, (S)ell, or (Q)uit?") 109 | user_input = input() 110 | if user_input in ['Q', 'q']: 111 | return 112 | elif user_input in ['B', 'b']: 113 | print("Here's whats available to buy: ") 114 | self.trade(buyer=player, seller=self.trader) 115 | elif user_input in ['S', 's']: 116 | print("Here's whats available to sell: ") 117 | self.trade(buyer=self.trader, seller=player) 118 | else: 119 | print("Invalid choice!") 120 | 121 | def trade(self, buyer, seller): 122 | for i, item in enumerate(seller.inventory, 1): 123 | print("{}. {} - {} Gold".format(i, item.name, item.value)) 124 | while True: 125 | user_input = input("Choose an item or press Q to exit: ") 126 | if user_input in ['Q', 'q']: 127 | return 128 | else: 129 | try: 130 | choice = int(user_input) 131 | to_swap = seller.inventory[choice - 1] 132 | self.swap(seller, buyer, to_swap) 133 | except ValueError: 134 | print("Invalid choice!") 135 | 136 | def swap(self, seller, buyer, item): 137 | if item.value > buyer.gold: 138 | print("That's too expensive") 139 | return 140 | seller.inventory.remove(item) 141 | buyer.inventory.append(item) 142 | seller.gold = seller.gold + item.value 143 | buyer.gold = buyer.gold - item.value 144 | print("Trade complete!") 145 | 146 | def intro_text(self): 147 | return """ 148 | A frail not-quite-human, not-quite-creature squats in the corner 149 | clinking his gold coins together. He looks willing to trade. 150 | """ 151 | 152 | world_dsl = """ 153 | |EN|EN|VT|EN|EN| 154 | |EN| | | |EN| 155 | |EN|FG|EN| |TT| 156 | |TT| |ST|FG|EN| 157 | |FG| |EN| |FG| 158 | """ 159 | 160 | 161 | def is_dsl_valid(dsl): 162 | if dsl.count("|ST|") != 1: 163 | return False 164 | if dsl.count("|VT|") == 0: 165 | return False 166 | lines = dsl.splitlines() 167 | lines = [l for l in lines if l] 168 | pipe_counts = [line.count("|") for line in lines] 169 | for count in pipe_counts: 170 | if count != pipe_counts[0]: 171 | return False 172 | 173 | return True 174 | 175 | tile_type_dict = {"VT": VictoryTile, 176 | "EN": EnemyTile, 177 | "ST": StartTile, 178 | "FG": FindGoldTile, 179 | "TT": TraderTile, 180 | " ": None} 181 | 182 | 183 | world_map = [] 184 | 185 | start_tile_location = None 186 | 187 | 188 | def parse_world_dsl(): 189 | if not is_dsl_valid(world_dsl): 190 | raise SyntaxError("DSL is invalid!") 191 | 192 | dsl_lines = world_dsl.splitlines() 193 | dsl_lines = [x for x in dsl_lines if x] 194 | 195 | for y, dsl_row in enumerate(dsl_lines): 196 | row = [] 197 | dsl_cells = dsl_row.split("|") 198 | dsl_cells = [c for c in dsl_cells if c] 199 | for x, dsl_cell in enumerate(dsl_cells): 200 | tile_type = tile_type_dict[dsl_cell] 201 | if tile_type == StartTile: 202 | global start_tile_location 203 | start_tile_location = x, y 204 | row.append(tile_type(x, y) if tile_type else None) 205 | 206 | world_map.append(row) 207 | 208 | 209 | def tile_at(x, y): 210 | if x < 0 or y < 0: 211 | return None 212 | try: 213 | return world_map[y][x] 214 | except IndexError: 215 | return None 216 | -------------------------------------------------------------------------------- /code/ch2_first/game.py: -------------------------------------------------------------------------------- 1 | print("Escape from Cave Terror!") 2 | -------------------------------------------------------------------------------- /code/ch3_io/echo.py: -------------------------------------------------------------------------------- 1 | user_input = input("Type some text: ") 2 | print(user_input) 3 | -------------------------------------------------------------------------------- /code/ch3_io/game.py: -------------------------------------------------------------------------------- 1 | print("Escape from Cave Terror!") 2 | action_input = input('Action: ') 3 | print(action_input) 4 | -------------------------------------------------------------------------------- /code/ch4_decisions/game.py: -------------------------------------------------------------------------------- 1 | print("Escape from Cave Terror!") 2 | action_input = input('Action: ') 3 | if action_input == 'n' or action_input == 'N': 4 | print("Go North!") 5 | elif action_input == 's' or action_input == 'S': 6 | print("Go South!") 7 | elif action_input == 'e' or action_input == 'E': 8 | print("Go East!") 9 | elif action_input == 'w' or action_input == 'W': 10 | print("Go West!") 11 | else: 12 | print("Invalid action!") 13 | -------------------------------------------------------------------------------- /code/ch5_functions/game.py: -------------------------------------------------------------------------------- 1 | def play(): 2 | print("Escape from Cave Terror!") 3 | action_input = get_player_command() 4 | if action_input == 'n' or action_input == 'N': 5 | print("Go North!") 6 | elif action_input == 's' or action_input == 'S': 7 | print("Go South!") 8 | elif action_input == 'e' or action_input == 'E': 9 | print("Go East!") 10 | elif action_input == 'w' or action_input == 'W': 11 | print("Go West!") 12 | else: 13 | print("Invalid action!") 14 | 15 | 16 | def get_player_command(): 17 | return input('Action: ') 18 | 19 | 20 | play() 21 | -------------------------------------------------------------------------------- /code/ch5_functions/hello.py: -------------------------------------------------------------------------------- 1 | def say_hello(): 2 | print("Hello, World!") 3 | 4 | say_hello() 5 | 6 | answer = input("Would you like another greeting?") 7 | if answer == 'y': 8 | say_hello() 9 | -------------------------------------------------------------------------------- /code/ch5_functions/hello_name.py: -------------------------------------------------------------------------------- 1 | def say_hello(name): 2 | print("Hello, " + name) 3 | 4 | user_name = input("What is your name? ") 5 | 6 | say_hello(user_name) 7 | -------------------------------------------------------------------------------- /code/ch6_lists/game.py: -------------------------------------------------------------------------------- 1 | def play(): 2 | inventory = ['Dagger','Gold(5)','Crusty Bread'] 3 | print("Escape from Cave Terror!") 4 | action_input = get_player_command() 5 | if action_input in ['n', 'N']: 6 | print("Go North!") 7 | elif action_input in ['s', 'S']: 8 | print("Go South!") 9 | elif action_input in ['e', 'E']: 10 | print("Go East!") 11 | elif action_input in ['w', 'W']: 12 | print("Go West!") 13 | elif action_input in ['i', 'I']: 14 | print("Inventory:") 15 | print(inventory) 16 | else: 17 | print("Invalid action!") 18 | 19 | 20 | def get_player_command(): 21 | return input('Action: ') 22 | 23 | 24 | play() 25 | -------------------------------------------------------------------------------- /code/ch7_loops/factors.py: -------------------------------------------------------------------------------- 1 | for i in range(1,11): 2 | factors = [] 3 | for j in range(1, i + 1): 4 | if i % j == 0: 5 | factors.append(j) 6 | print("The factors of " + str(i) + " are: " + str(factors)) 7 | -------------------------------------------------------------------------------- /code/ch7_loops/favorites.py: -------------------------------------------------------------------------------- 1 | favorites = [] 2 | more_items = True 3 | while more_items: 4 | user_input = input("Enter something you like: ") 5 | if user_input == '': 6 | more_items = False 7 | else: 8 | favorites.append(user_input) 9 | 10 | print("Here are all the things you like!") 11 | print(favorites) 12 | -------------------------------------------------------------------------------- /code/ch7_loops/favorites_pretty.py: -------------------------------------------------------------------------------- 1 | def pretty_print_unordered(to_print): 2 | for item in to_print: 3 | print("* " + str(item)) 4 | 5 | favorites = [] 6 | more_items = True 7 | while more_items: 8 | user_input = input("Enter something you like: ") 9 | if user_input == '': 10 | more_items = False 11 | else: 12 | favorites.append(user_input) 13 | 14 | print("Here are all the things you like!") 15 | pretty_print_unordered(favorites) 16 | -------------------------------------------------------------------------------- /code/ch7_loops/favorites_pretty_counter.py: -------------------------------------------------------------------------------- 1 | def pretty_print_ordered(to_print): 2 | i = 1 3 | for item in to_print: 4 | print(i + ". " + str(item)) 5 | i = i + 1 6 | 7 | favorites = [] 8 | more_items = True 9 | while more_items: 10 | user_input = input("Enter something you like: ") 11 | if user_input == '': 12 | more_items = False 13 | else: 14 | favorites.append(user_input) 15 | 16 | print("Here are all the things you like!") 17 | pretty_print_ordered(favorites) 18 | -------------------------------------------------------------------------------- /code/ch7_loops/favorites_pretty_enumerate.py: -------------------------------------------------------------------------------- 1 | def pretty_print_ordered(to_print): 2 | for i, value in enumerate(to_print, 1): 3 | print(str(i) + ". " + str(value)) 4 | 5 | favorites = [] 6 | more_items = True 7 | while more_items: 8 | user_input = input("Enter something you like: ") 9 | if user_input == '': 10 | more_items = False 11 | else: 12 | favorites.append(user_input) 13 | 14 | print("Here are all the things you like!") 15 | pretty_print_ordered(favorites) 16 | -------------------------------------------------------------------------------- /code/ch7_loops/favorites_pretty_range.py: -------------------------------------------------------------------------------- 1 | def pretty_print_ordered(to_print): 2 | for i in range(len(to_print)): 3 | print(str(i + 1) + ". " + str(to_print[i])) 4 | 5 | favorites = [] 6 | more_items = True 7 | while more_items: 8 | user_input = input("Enter something you like: ") 9 | if user_input == '': 10 | more_items = False 11 | else: 12 | favorites.append(user_input) 13 | 14 | print("Here are all the things you like!") 15 | pretty_print_ordered(favorites) 16 | -------------------------------------------------------------------------------- /code/ch7_loops/game.py: -------------------------------------------------------------------------------- 1 | def play(): 2 | inventory = ['Dagger','Gold(5)','Crusty Bread'] 3 | print("Escape from Cave Terror!") 4 | while True: 5 | action_input = get_player_command() 6 | if action_input in ['n', 'N']: 7 | print("Go North!") 8 | elif action_input in ['s', 'S']: 9 | print("Go South!") 10 | elif action_input in ['e', 'E']: 11 | print("Go East!") 12 | elif action_input in ['w', 'W']: 13 | print("Go West!") 14 | elif action_input in ['i', 'I']: 15 | print("Inventory:") 16 | for item in inventory: 17 | print('* ' + str(item)) 18 | else: 19 | print("Invalid action!") 20 | 21 | 22 | def get_player_command(): 23 | return input('Action: ') 24 | 25 | 26 | play() 27 | -------------------------------------------------------------------------------- /code/ch8_objects/census.py: -------------------------------------------------------------------------------- 1 | class Person: 2 | def __init__(self, name, age, favorite_foods): 3 | self.name = name 4 | self.age = age 5 | self.favorite_foods = favorite_foods 6 | 7 | def birth_year(self): 8 | return 2015 - self.age 9 | 10 | def __str__(self): 11 | return "Name: {} Age: {} Favorite food: {}".format( 12 | self.name, self.age, self.favorite_foods[0]) 13 | 14 | people = [Person("Ed", 11, ["hotdogs", "jawbreakers"]) 15 | , Person("Edd", 11, ["broccoli"]) 16 | , Person("Eddy", 12, ["chunky puffs", "jawbreakers"])] 17 | 18 | age_sum = 0 19 | year_sum = 0 20 | for person in people: 21 | age_sum = age_sum + person.age 22 | year_sum = year_sum + person.birth_year() 23 | 24 | print("The average age is: " + str(age_sum / len(people))) 25 | print("The average birth year is: " + str(int(year_sum / len(people)))) 26 | 27 | print("The people polled in this census were:") 28 | for person in people: 29 | print(person) 30 | -------------------------------------------------------------------------------- /code/ch8_objects/game.py: -------------------------------------------------------------------------------- 1 | class Weapon: 2 | def __str__(self): 3 | return self.name 4 | 5 | 6 | class Rock(Weapon): 7 | def __init__(self): 8 | self.name = "Rock" 9 | self.description = "A fist-sized rock, suitable for bludgeoning." 10 | self.damage = 5 11 | 12 | 13 | class Dagger(Weapon): 14 | def __init__(self): 15 | self.name = "Dagger" 16 | self.description = "A small dagger with some rust. " \ 17 | "Somewhat more dangerous than a rock." 18 | self.damage = 10 19 | 20 | 21 | class RustySword(Weapon): 22 | def __init__(self): 23 | self.name = "Rusty sword" 24 | self.description = "This sword is showing its age, " \ 25 | "but still has some fight in it." 26 | self.damage = 20 27 | 28 | 29 | def play(): 30 | inventory = [Dagger(),'Gold(5)','Crusty Bread'] 31 | print("Escape from Cave Terror!") 32 | while True: 33 | action_input = get_player_command() 34 | if action_input in ['n', 'N']: 35 | print("Go North!") 36 | elif action_input in ['s', 'S']: 37 | print("Go South!") 38 | elif action_input in ['e', 'E']: 39 | print("Go East!") 40 | elif action_input in ['w', 'W']: 41 | print("Go West!") 42 | elif action_input in ['i', 'I']: 43 | print("Inventory:") 44 | for item in inventory: 45 | print('* ' + str(item)) 46 | else: 47 | print("Invalid action!") 48 | 49 | 50 | def get_player_command(): 51 | return input('Action: ') 52 | 53 | 54 | play() 55 | -------------------------------------------------------------------------------- /code/ch9_exceptions/game.py: -------------------------------------------------------------------------------- 1 | class Weapon: 2 | def __init__(self): 3 | raise NotImplementedError("Do not create raw Weapon objects.") 4 | 5 | def __str__(self): 6 | return self.name 7 | 8 | 9 | class Rock(Weapon): 10 | def __init__(self): 11 | self.name = "Rock" 12 | self.description = "A fist-sized rock, suitable for bludgeoning." 13 | self.damage = 5 14 | 15 | 16 | class Dagger(Weapon): 17 | def __init__(self): 18 | self.name = "Dagger" 19 | self.description = "A small dagger with some rust. " \ 20 | "Somewhat more dangerous than a rock." 21 | self.damage = 10 22 | 23 | 24 | class RustySword(Weapon): 25 | def __init__(self): 26 | self.name = "Rusty sword" 27 | self.description = "This sword is showing its age, " \ 28 | "but still has some fight in it." 29 | self.damage = 20 30 | 31 | 32 | def play(): 33 | inventory = [Rock(), Dagger(), 'Gold(5)', 'Crusty Bread'] 34 | print("Escape from Cave Terror!") 35 | while True: 36 | action_input = get_player_command() 37 | if action_input in ['n', 'N']: 38 | print("Go North!") 39 | elif action_input in ['s', 'S']: 40 | print("Go South!") 41 | elif action_input in ['e', 'E']: 42 | print("Go East!") 43 | elif action_input in ['w', 'W']: 44 | print("Go West!") 45 | elif action_input in ['i', 'I']: 46 | print("Inventory:") 47 | for item in inventory: 48 | print('* ' + str(item)) 49 | best_weapon = most_powerful_weapon(inventory) 50 | print("Your best weapon is your {}".format(best_weapon)) 51 | else: 52 | print("Invalid action!") 53 | 54 | 55 | def get_player_command(): 56 | return input('Action: ') 57 | 58 | 59 | def most_powerful_weapon(inventory): 60 | max_damage = 0 61 | best_weapon = None 62 | for item in inventory: 63 | try: 64 | if item.damage > max_damage: 65 | best_weapon = item 66 | max_damage = item.damage 67 | except AttributeError: 68 | pass 69 | 70 | return best_weapon 71 | 72 | play() 73 | -------------------------------------------------------------------------------- /code/ch9_exceptions/validate.py: -------------------------------------------------------------------------------- 1 | name = input("Please enter your name: ") 2 | age = input("Please enter your age: ") 3 | try: 4 | print("You were born in {}.".format(2015 - int(age))) 5 | except ValueError: 6 | print('Unable to calculate the year you were born, ' \ 7 | + '"{}" is not a number.'.format(age)) 8 | -------------------------------------------------------------------------------- /code/hw-solutions/ch2_first/calculator.py: -------------------------------------------------------------------------------- 1 | print("Which numbers do you want to add?") 2 | -------------------------------------------------------------------------------- /code/hw-solutions/ch3_io/echo.py: -------------------------------------------------------------------------------- 1 | print(input("Type some text: ")) 2 | -------------------------------------------------------------------------------- /code/hw-solutions/ch4_decisions/ages.py: -------------------------------------------------------------------------------- 1 | age = int(input("What is your age? ")) 2 | if age < 18: 3 | print("You are a child.") 4 | elif 18 < age < 21: 5 | print("You are an adult, but you cannot purchase alcohol.") 6 | else: 7 | print("You are an adult.") 8 | if age >= 16: 9 | print("You are allowed to drive.") 10 | else: 11 | print("You are not allowed to drive") 12 | -------------------------------------------------------------------------------- /code/hw-solutions/ch5_functions/calculator.py: -------------------------------------------------------------------------------- 1 | def add(a, b): 2 | return a + b 3 | 4 | print(add(45, 55)) 5 | -------------------------------------------------------------------------------- /code/hw-solutions/ch5_functions/doubler.py: -------------------------------------------------------------------------------- 1 | def double(a): 2 | return a * 2 3 | 4 | print(double(12345)) 5 | print(double(1.57)) 6 | -------------------------------------------------------------------------------- /code/hw-solutions/ch5_functions/user_calculator.py: -------------------------------------------------------------------------------- 1 | def add(a, b): 2 | return a + b 3 | 4 | num1 = int(input("Please enter your 1st number: ")) 5 | num2 = int(input("Please enter your 2nd number: ")) 6 | 7 | print(add(num1, num2)) 8 | -------------------------------------------------------------------------------- /code/hw-solutions/ch6_lists/favorites.py: -------------------------------------------------------------------------------- 1 | favorites = [] 2 | favorites.append(input("What is your favorite food? ")) 3 | favorites.append(input("What is your 2nd favorite food? ")) 4 | favorites.append(input("What is your 3rd favorite food? ")) 5 | -------------------------------------------------------------------------------- /code/hw-solutions/ch7_loops/greek.py: -------------------------------------------------------------------------------- 1 | letters = ['alpha','beta','gamma','delta','epsilon','zeta','eta'] 2 | for i, letter in enumerate(letters): 3 | if i % 3 == 0: 4 | print(letter) 5 | -------------------------------------------------------------------------------- /code/hw-solutions/ch7_loops/multiplication.py: -------------------------------------------------------------------------------- 1 | for i in range(1, 11): 2 | line = "" 3 | for j in range(1, 11): 4 | line = line + str(i * j) + " " 5 | print(line) 6 | -------------------------------------------------------------------------------- /code/hw-solutions/ch7_loops/user_calculator.py: -------------------------------------------------------------------------------- 1 | def add(a, b): 2 | return a + b 3 | 4 | while True: 5 | num1 = int(input("Please enter your 1st number: ")) 6 | num2 = int(input("Please enter your 2nd number: ")) 7 | 8 | print(add(num1, num2)) 9 | -------------------------------------------------------------------------------- /code/hw-solutions/ch8_objects/food.py: -------------------------------------------------------------------------------- 1 | class Food: 2 | def __init__(self, name, carbs, protein, fat): 3 | self.name = name 4 | self.carbs = carbs 5 | self.protein = protein 6 | self.fat = fat 7 | 8 | def calories(self): 9 | return self.carbs * 4 + self.protein * 4 + self.fat * 9 10 | 11 | 12 | class Recipe: 13 | def __init__(self, name, ingredients): 14 | self.name = name 15 | self.ingredients = ingredients 16 | 17 | def calories(self): 18 | total = 0 19 | for ingredient in self.ingredients: 20 | total = total + ingredient.calories() 21 | 22 | return total 23 | 24 | def __str__(self): 25 | return self.name 26 | 27 | pbj = Recipe("Peanut Butter & Jelly", [ 28 | Food(name="Peanut Butter", carbs=6, protein=8, fat=16), 29 | Food(name="Jelly", carbs=13, protein=0, fat=0), 30 | Food(name="Bread", carbs=24, protein=7, fat=2)] 31 | ) 32 | 33 | omelette = Recipe("Omelette du Fromage", [ 34 | Food(name="Eggs", carbs=3, protein=18, fat=15), 35 | Food(name="Cheese", carbs=5, protein=24, fat=24) 36 | ]) 37 | 38 | recipes = [pbj, omelette] 39 | 40 | for recipe in recipes: 41 | print("{}: {} calories".format(recipe.name, recipe.calories())) 42 | -------------------------------------------------------------------------------- /code/hw-solutions/ch9_exceptions/user_calculator.py: -------------------------------------------------------------------------------- 1 | def add(a, b): 2 | return a + b 3 | 4 | while True: 5 | try: 6 | num1 = int(input("Please enter your 1st number: ")) 7 | num2 = int(input("Please enter your 2nd number: ")) 8 | 9 | print(add(num1, num2)) 10 | except ValueError: 11 | print("You must enter a number.") 12 | -------------------------------------------------------------------------------- /code/hw-solutions/ch9_exceptions/vehicles.py: -------------------------------------------------------------------------------- 1 | class Vehicle: 2 | def __init__(self): 3 | raise NotImplementedError("You must use a subclass.") 4 | 5 | 6 | class Motorcycle(Vehicle): 7 | def __init__(self): 8 | self.wheels = 2 9 | 10 | 11 | class Car(Vehicle): 12 | def __init__(self): 13 | self.wheels = 4 14 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Apress Source Code 2 | 3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. 4 | 5 | ## How to Contribute 6 | 7 | 1. Make sure you have a GitHub account. 8 | 2. Fork the repository for the relevant book. 9 | 3. Create a new branch on which to make your change, e.g. 10 | `git checkout -b my_code_contribution` 11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 12 | 5. Submit a pull request. 13 | 14 | Thank you for your contribution! --------------------------------------------------------------------------------