Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
17511b4
[format] PEP8 fixes
ngjunsiang Aug 28, 2024
286da95
[fix] Remove use of eval()
ngjunsiang Oct 3, 2024
33d83af
[refactor] use prompt_choice() helper function
ngjunsiang Oct 3, 2024
0b941b5
[refactor] collapse menu scripts
ngjunsiang Oct 3, 2024
51469f1
[refactor] collapse menu methods in Interface
ngjunsiang Oct 3, 2024
1edb5a6
[cleanup] Remove unused code
ngjunsiang Oct 3, 2024
a29a7d1
[refactor] Eliminate Interface class
ngjunsiang Oct 3, 2024
abeeee9
[feat] Input validation for prompt_choice()
ngjunsiang Oct 3, 2024
d8f6a03
[refactor] Merge game data into gamedata.json
ngjunsiang Oct 3, 2024
8b1f770
[refactor] Split isover() and add endgame()
ngjunsiang Oct 3, 2024
17d0fd5
[refactor] Break up game loop
ngjunsiang Oct 3, 2024
b7977d9
[format] cleanup formatting
ngjunsiang Oct 3, 2024
579cfd4
[refactor] Use encapsulation
ngjunsiang Oct 3, 2024
6196649
[refactor] collapse player_attack() and enemy_attack() into a single …
ngjunsiang Oct 3, 2024
ebf00ae
[fix] swap attacker and defender after each cycle
ngjunsiang Oct 3, 2024
37d000b
[refactor] Flatten code
ngjunsiang Oct 3, 2024
63065e6
[refactor] add soldier data into gamedata.json
ngjunsiang Oct 3, 2024
2b634e5
[refactor] Use createSoldier() factory function
ngjunsiang Oct 3, 2024
651ede2
[refactor] Polymorphism: implement get_type() for Character subclasses
ngjunsiang Oct 3, 2024
8f0e334
[docs] cleanup; add method annotations
ngjunsiang Oct 3, 2024
07c8efa
[refactor] generalise death msg display
ngjunsiang Oct 3, 2024
22cf167
[fix] enemy removal checks if attacker is player
ngjunsiang Oct 3, 2024
997655b
[fix] fix logic error
ngjunsiang Oct 3, 2024
b04ebdd
[refactor] Move damage logic to Battle
ngjunsiang Oct 3, 2024
af214f2
[cleanup] remove unused code
ngjunsiang Oct 3, 2024
3dec349
[refactor] separate attack and report
ngjunsiang Oct 3, 2024
3b1c2b4
[add] module variable report
ngjunsiang Oct 3, 2024
14cc8db
[refactor] Use report() to handle output
ngjunsiang Oct 3, 2024
1f2c5f6
[refactor] use interface.report to handle output
ngjunsiang Oct 3, 2024
fe8fc2b
[test] Set interface to log to file during testing
ngjunsiang Oct 4, 2024
f6ad3db
[test] Build game testing script
ngjunsiang Oct 4, 2024
4e50d33
[fix] resolve key error
ngjunsiang Oct 4, 2024
57afe16
[fix] add 'message' for 'start'
ngjunsiang Oct 4, 2024
213b000
[refactor] Move start menu to main
ngjunsiang Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions Ascii.py

This file was deleted.

16 changes: 16 additions & 0 deletions Test.py
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@
import ctps
import interface

LOGFILE = "test.log"

def log_to_file(message: str) -> None:
with open(LOGFILE, "a") as f:
f.write(message + "\n")

interface.report = log_to_file

commands = ['start', 'dungeon', 'kitchen']

game = ctps.Game()
game.setup()
for choice in commands:
game.do_choice(choice)
68 changes: 32 additions & 36 deletions battle.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,44 @@
import time

import character
import interface


class Battle:

def __init__(self, player, room):
self.player = player
self.room = room


def battle_start(self):
if not self.room.all_enemies_defeated():
print("YOU FOUND ENEMIES!!!!")
i = 0
while not self.battle_over():
if i % 2 == 0:
self.player_attack()
elif i % 2 == 1:
self.enemy_attack()

i += 1
time.sleep(0.1)
print("Battle over!")
else:
print("All soldiers in the room have been defeated already!")

def player_attack(self):
source = self.player
target = self.room.get_enemies()[0]
source.attack(target)
print(f"You attacked the {target.get_type()}! {target.get_type()} health: {target.get_health()}")
if target.get_health() <= 0:
if target.get_type() == "Princess":
print("Princess is now unconcious! Time to escape!")
else:
print("Soldier defeated!")
self.room.remove_enemy()
if self.room.all_enemies_defeated():
print("All soldiers in the room are defeated!")
interface.report("All soldiers in the room have been defeated already!")
return
interface.report("YOU FOUND ENEMIES!!!!")
while not self.battle_over():
attacker = self.player
defender = self.room.get_enemies()[0]
self.attack(attacker, defender)
if defender.isdead():
interface.report(interface.death_msg(defender.get_type()))
if attacker == self.player:
self.room.remove_enemy()
defender = self.room.get_enemies()[0]
attacker, defender = defender, attacker
time.sleep(0.1)
if self.room.all_enemies_defeated():
interface.report("All soldiers in the room are defeated!")
interface.report("Battle over!")

def attack(self, attacker, defender) -> None:
defender.receive_damage(attacker.damage)
interface.report(self.attack_report(attacker, defender))

def enemy_attack(self):
source = self.room.get_enemies()[0]
target = self.player
source.attack(target)
print(f"{source.get_type()} attacked you! Your health: {target.get_health()}")
# if target.get_health() <= 0:
# print("You died! WEAK!")
def attack_report(self, attacker, defender) -> str:
if attacker.get_type() == "Player":
return f"You attacked the {defender.get_type()}! {defender.get_type()} health: {defender.get_health()}"
else:
return f"{attacker.get_type()} attacked you! Your health: {defender.get_health()}"

def battle_over(self):
return self.player.get_health() <= 0 or self.room.all_enemies_defeated()
return self.player.isdead() or self.room.all_enemies_defeated()
39 changes: 13 additions & 26 deletions character.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,39 @@
import random

class Character:

def __init__(self, _type, health, damage = 0):
self._type = _type
def __init__(self, health, damage=0):
self.health = health
self.damage = damage


def attack(self, character):
"""
Deal damage to another character object
"""
character.receive_damage(self.damage)
# print("Die")

def receive_damage(self, damage):
def receive_damage(self, damage: int) -> None:
"""
Remove health
"""
self.health -= damage
# print("Ouch")

def isdead(self):
def isdead(self) -> bool:
"""
Returns status of character (dead or alive)
"""
return self.health <= 0

def get_health(self):
def get_health(self) -> int:
return self.health

def get_type(self):
return self._type
def get_type(self) -> str:
raise NotImplementedError

class Player(Character):

def __init__(self, health, damage):
super().__init__("Player", health, damage)
class Player(Character):
def get_type(self) -> str:
return "Player"


class Soldier(Character):
def get_type(self) -> str:
return "Soldier"

def __init__(self, health):
super().__init__("Soldier", health)
self.damage = random.randint(2,7)

class Princess(Character):

def __init__(self, health):
super().__init__("Princess", health)
def get_type(self) -> str:
return "Princess"
59 changes: 31 additions & 28 deletions ctps.py
Original file line number Diff line number Diff line change
@@ -1,74 +1,77 @@
import data, interface, battle
import data
import interface
import battle


class Game:

def __init__(self):
self.interface = None #
self.player = None
self.princess = None
self.rooms = []
self.now = 0

def setup(self):
self.interface = interface.Interface() #
self.player = data.createPlayer()
self.rooms = data.createRooms()
self.princess = self.rooms[-1].get_enemies()[-1]
if self.interface.start_menu() == 'exit': #if interface.start_menu() == 'exit':
self.interface.exit_screen() #interface.exit_screen()
exit()

def isover(self):

def isover(self) -> bool:
if not self.player or not self.princess:
# Game has not been set up
return False
return self.player.isdead() or self.princess.isdead()

def end_game(self) -> None:
if not self.player or not self.princess:
# Game has not been set up
return
if self.player.isdead():
self.interface.death_msg() #interface.death_msg()
return True
pass # death msg already displayed in battle
elif self.princess.isdead():
if all([room.all_enemies_defeated() for room in self.rooms]):
self.interface.win_msg() #interface.win_msg()
interface.win_msg()
else:
self.interface.caught_msg() #interface.caught_msg()
return True
return False
interface.caught_msg()

def next_room(self):
if self.now < len(self.rooms) - 1:
self.now += 1

def prev_room(self):
if self.now > 0:
self.now -= 1

def get_now_room(self):
return self.rooms[self.now]

def get_now_room_name(self):
return self.rooms[self.now].get_name()

def get_next_room_name(self):
if self.now < len(self.rooms) - 1:
return self.rooms[self.now + 1].get_name()
else:
return "WALL"

def get_prev_room_name(self):
if self.now - 1 >= 0:
return self.rooms[self.now - 1].get_name()
else:
return "WALL"

def get_choice(self):
"Dispalys and gets player choice. Display results afterwards"
choice = eval(f"self.interface.{self.get_now_room_name().lower()}_menu()") #choice = eval(f"interface.{self.get_now_room_name().lower()}_menu()")
print(choice+'\n')
def get_choice(self) -> str:
"""Prompts player to make a choice."""
room_name = self.get_now_room_name().lower()
choice = interface.prompt_menu(room_name)
return choice

def do_choice(self, choice: str) -> None:
print("Your choice:", choice)
if choice == 'Move to next room':
self.next_room()
elif choice == 'Move to previous room':
self.prev_room()
elif choice == 'Look around':

combat = battle.Battle(self.player, self.get_now_room())
combat.battle_start()

else:
print("Invalid choice")
print()

11 changes: 0 additions & 11 deletions data.json

This file was deleted.

44 changes: 19 additions & 25 deletions data.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,38 @@
import json
import random

import room
import character
import json

with open('gamedata.json', 'r') as f:
gamedata = json.load(f)


def createRooms() -> list[room.Room]:
"""
Must follow order of room: Dungeon, Kitchen, Hall, Toilet, Bedroom
"""Must follow order of room: Dungeon, Kitchen, Hall, Toilet, Bedroom
Store Name & Number of enemies
"""

list_of_rooms = []
room_name_enemy_num = {
"Dungeon": 3,
"Kitchen": 3,
"Hall": 3,
"Toilet": 3,
"Bedroom": 3
}
for name, num in room_name_enemy_num.items():
for name, roomdata in gamedata["room"].items():
# breakpoint()
temp = room.Room(name)
for _ in range(num):
temp.add_enemy(character.Soldier(20))
for _ in range(roomdata["num_enemies"]):
temp.add_enemy(createSoldier())
list_of_rooms.append(temp)
temp.add_enemy(character.Princess(1))

return list_of_rooms


with open('data.json', 'r') as f:
char_data = json.load(f)


def createPlayer() -> character.Player:
"""

"""
record = char_data["player"]
record = gamedata["player"]
return character.Player(record["hp"], record["str"])


def createPrincess() -> character.Princess:
record = char_data["princess"]
return character.Princess(record["hp"])
record = gamedata["princess"]
return character.Princess(record["hp"])

def createSoldier() -> character.Soldier:
record = gamedata["soldier"]
min_str, max_str = record["str"]
return character.Soldier(record["hp"], random.randint(min_str, max_str))
22 changes: 22 additions & 0 deletions gamedata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"player": {
"hp": 300,
"str": 5,
"items": []
},
"princess": {
"hp": 20,
"str": 0
},
"soldier": {
"hp": 20,
"str": [2, 7]
},
"room": {
"Dungeon": {"num_enemies": 3},
"Kitchen": {"num_enemies": 3},
"Hall": {"num_enemies": 3},
"Toilet": {"num_enemies": 3},
"Bedroom": {"num_enemies": 3}
}
}
Loading