Text-Turn based dueling game
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
5
down vote
favorite
I have recently started to learn python, and I have made my first fighting duel game. I'm planning on adding more stuff in the future like shops and items so you get a way to spend gold, but for now the current features are:
Character creation (with saving and loading),
Upgrading Str/Def,- Keeping Score
Battling AI or Battling 1v1.- (you also get rewards for winning)
But for now I'm happy with my progress. Please review my code!
import random
import json
import time
r = random
#INSTRUCTIONS FOR FIRST TIME USE
#MAKE 3 COMPUTER ACCOUNTS NAMED "Computer_Easy", "Computer_Med" and "Computer_Hard" AND SET THEIR STATS HOWEVER YOU LIKE! OTHERWISE UNABLE TO PLAY VS AI
class Character():
#Change value of default if you want people to start with higher str/def when they make their account.
def __init__(self, name, default=1, health=None, strength=None,
defence=None, gold=0, exp=0, wins=0, losses=0,
):
self.name = name
self.health = 100
self.strength = default if strength is None else strength
self.defence = default if defence is None else defence
self.gold = 0
self.exp = 0
self.wins = 0
self.losses = 0
def make_account(self, name): #Create a new user to /accounts folder with default stats as listed above
save_name = name
path = 'accounts/0.json'.format(save_name)
data =
"health": self.health,
"strength": self.strength,
"defence": self.defence,
"gold": self.gold,
"exp": self.exp,
"wins": self.wins,
"losses": self.losses,
with open(path, 'w+') as f:
json.dump(data, f)
def load_account(self, name):
load_name = name
path_two = 'accounts/0.json'.format(load_name)
with open(path_two, 'r') as f:
data = json.load(f)
return data
def upgrade_stat(self, name): #Upgrade either defence or strength
target = raw_input("What stat do you want to upgrade?")
if target in "health", "gold", "exp", "wins", "losses":
print("you cannot do that!")
return
load_name = name
path_two = 'accounts/0.json'.format(load_name)
with open(path_two, 'r') as f:
data = json.load(f)
if data["exp"] < 100:
print("You don't have enough xp!")
else:
data["exp"] -= 100
data[target] += 1
with open (path_two, 'w+') as g:
json.dump(data, g)
def calculate_damage(self, damage_amount, attacker, defender):
attacker_stats = self.load_account(name=attacker)
defender_stats = self.load_account(name=defender)
#You get damage boost, depending on attacker strength level and defender defence level.
damage_amount_boost = (attacker_stats["strength"] * damage_amount / (attacker_stats["strength"] + defender_stats["defence"])) + damage_amount
if (damage_amount_boost > self.health):
overkill = abs(self.health - damage_amount_boost)
self.health = 0
if (overkill > 0):
print("0 takes fatal damage from 1, with 2 overkill!"
.format(self.name.capitalize(), attacker, overkill))
else:
print("0 takes fatal damage from 1!"
.format(self.name.capitalize(), attacker))
else:
self.health -= damage_amount_boost
print("0 takes 1 damage from 2!"
.format(self.name.capitalize(), damage_amount_boost, attacker))
def calculate_heal(self, heal_amount):
if (heal_amount + self.health > 100):
self.health = 100
print("0 heals back to full health!"
.format(self.name.capitalize()))
else:
self.health += heal_amount
print("0 heals for 1!"
.format(self.name.capitalize(), heal_amount))
def parse_int(input):
try:
int(input)
return True
except ValueError:
return False
def get_selection():
valid_input = False
while (valid_input is False):
print()
choice = input("Select an attack: ")
if (parse_int(choice) is True):
return int(choice)
else:
print("The input was invalid. Please try again.")
def get_computer_selection(health):
sleep_time = random.randrange(2, 5)
print("Computer thinking...")
time.sleep(sleep_time)
if (health <= 35):
# Have the computer heal ~50% of its turns when <= 35
result = random.randint(1, 6)
if (result % 2 == 0):
return 3
else:
return random.randint(1, 2)
elif (health == 100):
return random.randint(1, 2)
else:
return random.randint(1, 3)
def play_round_ai(computer, human):
game_in_progress = True
current_player = computer
movesprint = '1. Mild, 2. Hard, 3. Heal'
while game_in_progress:
if (current_player == computer):
current_player = human
else:
current_player = computer
print(
"You have 0 health remaining and the "
"computer has 1 health remaining."
.format(human.health, computer.health))
if (current_player == human):
print(movesprint)
move = get_selection()
else:
move = get_computer_selection(computer.health)
if (move == 1):
damage = random.randrange(12, 22)
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
elif (move == 2):
damage = random.randrange(5, 35)
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
elif (move == 3):
heal = random.randrange(15, 28)
current_player.calculate_heal(heal)
else:
print ("The input was not valid. Please select a choice again.")
if (human.health == 0):
print("Sorry, you lose!")
computer.wins += 1
load_names = human.name
paths = 'accounts/0.json'.format(load_names)
with open(paths, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(paths, 'w+') as g:
json.dump(data, g)
game_in_progress = False
if (computer.health == 0):
print("Congratulations, you beat the computer!")
human.wins += 1
load_names = human.name
paths = 'accounts/0.json'.format(load_names)
with open(paths, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(paths, 'w+') as g:
json.dump(data, g)
game_in_progress = False
def play_round_multiplayer(player2, player1):
game_in_progress = True
current_player = player2
movesprint = '1. Mild, 2. Hard, 3. Heal'
while game_in_progress:
if (current_player == player2):
current_player = player1
else:
current_player = player2
#print()
print(
"You have 0 health remaining and the "
"player2 has 1 health remaining."
.format(player1.health, player2.health))
#print()
if (current_player == player1):
print(movesprint)
move = get_selection()
else:
print(movesprint)
move = get_selection()
if (move == 1):
damage = random.randrange(12, 22)
if (current_player == player1):
player2.calculate_damage(damage, player1.name, player2.name)
else:
player1.calculate_damage(damage, player2.name, player1.name)
elif (move == 2):
damage = random.randrange(5, 35)
if (current_player == player1):
player2.calculate_damage(damage, player1.name, player2.name)
else:
player1.calculate_damage(damage, player2.name, player1.name)
elif (move == 3):
heal = random.randrange(15, 28)
current_player.calculate_heal(heal)
else:
print ("The input was not valid. Please select a choice again.")
if (player1.health == 0):
print("0 wins".format(player2.name))
player2.wins += 1
winner_name = player2.name
loser_name = player1.name
winner_path = 'accounts/0.json'.format(winner_name)
loser_path = 'accounts/0.json'.format(loser_name)
with open(winner_path, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(winner_path, 'w+') as g:
json.dump(data, g)
with open(loser_path, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(loser_path, 'w+') as g:
json.dump(data, g)
game_in_progress = False
if (player2.health == 0):
print("0 wins".format(player1.name))
player1.wins += 1
winner_name = player1.name
loser_name = player2.name
winner_path = 'accounts/0.json'.format(winner_name)
loser_path = 'accounts/0.json'.format(loser_name)
with open(winner_path, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(winner_path, 'w+') as g:
json.dump(data, g)
with open(loser_path, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(loser_path, 'w+') as g:
json.dump(data, g)
game_in_progress = False
def start_game_ai(name): #Play vs Ai, which you select your difficulty.
target = raw_input("Easy, med or hard?")
if target == "easy":
computer = Character("Computer_Easy") #Make sure you have made all of these accounts in folder Accounts
if target == "med": #Set stats however you like
computer = Character("Computer_Med")
if target == "hard":
computer = Character("Computer_Hard")
human = Character(name)
keep_playing = True
while (keep_playing is True):
print("Current Score:")
print("You - 0".format(human.wins))
print("Computer - 0".format(computer.wins))
computer.health = 100
human.health = 100
play_round_ai(computer, human)
print()
response = raw_input("Play another round?(Y/N)")
if (response.lower() == "n"):
break
def start_game_multiplayer(name, target): #Play a local 1v1 game.
player2 = Character(target)
player1 = Character(name)
keep_playing = True
while (keep_playing is True):
print("Current Score:")
print("Player1 - 0".format(player1.wins))
print("Player2 - 0".format(player2.wins))
player2.health = 100
player1.health = 100
play_round_multiplayer(player2, player1)
#print()
response = raw_input("Play another 1v1? (y/n)")
if (response == "y"):
continue
else:
break
def options(): #Main menu
name = raw_input("Username to load or create?")
print("What do you want to do? If this is your first time, type 5 to create your new account!")
move = raw_input("1. Battle AI | 2. List stats | 3. Play 1v1 | 4. Upgrade str/def | 5. Make new user")
if move == "1":
print("Make sure you already have made all Computer accounts! (Computer_Easy, Computer_Med, Computer_Hard)")
start_game_ai(name)
if move == "2":
stats = Character(name).load_account(name)
print stats
return
if move == "3":
target = raw_input("Who do you want to fight?")
start_game_multiplayer(name, target)
if move == "4":
Character(name).upgrade_stat(name)
stats = Character(name).load_account(name)
print stats
if move == "5":
Character(name).make_account(name)
else:
print("Exiting..")
return
options()
python python-2.7 battle-simulation
add a comment |Â
up vote
5
down vote
favorite
I have recently started to learn python, and I have made my first fighting duel game. I'm planning on adding more stuff in the future like shops and items so you get a way to spend gold, but for now the current features are:
Character creation (with saving and loading),
Upgrading Str/Def,- Keeping Score
Battling AI or Battling 1v1.- (you also get rewards for winning)
But for now I'm happy with my progress. Please review my code!
import random
import json
import time
r = random
#INSTRUCTIONS FOR FIRST TIME USE
#MAKE 3 COMPUTER ACCOUNTS NAMED "Computer_Easy", "Computer_Med" and "Computer_Hard" AND SET THEIR STATS HOWEVER YOU LIKE! OTHERWISE UNABLE TO PLAY VS AI
class Character():
#Change value of default if you want people to start with higher str/def when they make their account.
def __init__(self, name, default=1, health=None, strength=None,
defence=None, gold=0, exp=0, wins=0, losses=0,
):
self.name = name
self.health = 100
self.strength = default if strength is None else strength
self.defence = default if defence is None else defence
self.gold = 0
self.exp = 0
self.wins = 0
self.losses = 0
def make_account(self, name): #Create a new user to /accounts folder with default stats as listed above
save_name = name
path = 'accounts/0.json'.format(save_name)
data =
"health": self.health,
"strength": self.strength,
"defence": self.defence,
"gold": self.gold,
"exp": self.exp,
"wins": self.wins,
"losses": self.losses,
with open(path, 'w+') as f:
json.dump(data, f)
def load_account(self, name):
load_name = name
path_two = 'accounts/0.json'.format(load_name)
with open(path_two, 'r') as f:
data = json.load(f)
return data
def upgrade_stat(self, name): #Upgrade either defence or strength
target = raw_input("What stat do you want to upgrade?")
if target in "health", "gold", "exp", "wins", "losses":
print("you cannot do that!")
return
load_name = name
path_two = 'accounts/0.json'.format(load_name)
with open(path_two, 'r') as f:
data = json.load(f)
if data["exp"] < 100:
print("You don't have enough xp!")
else:
data["exp"] -= 100
data[target] += 1
with open (path_two, 'w+') as g:
json.dump(data, g)
def calculate_damage(self, damage_amount, attacker, defender):
attacker_stats = self.load_account(name=attacker)
defender_stats = self.load_account(name=defender)
#You get damage boost, depending on attacker strength level and defender defence level.
damage_amount_boost = (attacker_stats["strength"] * damage_amount / (attacker_stats["strength"] + defender_stats["defence"])) + damage_amount
if (damage_amount_boost > self.health):
overkill = abs(self.health - damage_amount_boost)
self.health = 0
if (overkill > 0):
print("0 takes fatal damage from 1, with 2 overkill!"
.format(self.name.capitalize(), attacker, overkill))
else:
print("0 takes fatal damage from 1!"
.format(self.name.capitalize(), attacker))
else:
self.health -= damage_amount_boost
print("0 takes 1 damage from 2!"
.format(self.name.capitalize(), damage_amount_boost, attacker))
def calculate_heal(self, heal_amount):
if (heal_amount + self.health > 100):
self.health = 100
print("0 heals back to full health!"
.format(self.name.capitalize()))
else:
self.health += heal_amount
print("0 heals for 1!"
.format(self.name.capitalize(), heal_amount))
def parse_int(input):
try:
int(input)
return True
except ValueError:
return False
def get_selection():
valid_input = False
while (valid_input is False):
print()
choice = input("Select an attack: ")
if (parse_int(choice) is True):
return int(choice)
else:
print("The input was invalid. Please try again.")
def get_computer_selection(health):
sleep_time = random.randrange(2, 5)
print("Computer thinking...")
time.sleep(sleep_time)
if (health <= 35):
# Have the computer heal ~50% of its turns when <= 35
result = random.randint(1, 6)
if (result % 2 == 0):
return 3
else:
return random.randint(1, 2)
elif (health == 100):
return random.randint(1, 2)
else:
return random.randint(1, 3)
def play_round_ai(computer, human):
game_in_progress = True
current_player = computer
movesprint = '1. Mild, 2. Hard, 3. Heal'
while game_in_progress:
if (current_player == computer):
current_player = human
else:
current_player = computer
print(
"You have 0 health remaining and the "
"computer has 1 health remaining."
.format(human.health, computer.health))
if (current_player == human):
print(movesprint)
move = get_selection()
else:
move = get_computer_selection(computer.health)
if (move == 1):
damage = random.randrange(12, 22)
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
elif (move == 2):
damage = random.randrange(5, 35)
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
elif (move == 3):
heal = random.randrange(15, 28)
current_player.calculate_heal(heal)
else:
print ("The input was not valid. Please select a choice again.")
if (human.health == 0):
print("Sorry, you lose!")
computer.wins += 1
load_names = human.name
paths = 'accounts/0.json'.format(load_names)
with open(paths, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(paths, 'w+') as g:
json.dump(data, g)
game_in_progress = False
if (computer.health == 0):
print("Congratulations, you beat the computer!")
human.wins += 1
load_names = human.name
paths = 'accounts/0.json'.format(load_names)
with open(paths, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(paths, 'w+') as g:
json.dump(data, g)
game_in_progress = False
def play_round_multiplayer(player2, player1):
game_in_progress = True
current_player = player2
movesprint = '1. Mild, 2. Hard, 3. Heal'
while game_in_progress:
if (current_player == player2):
current_player = player1
else:
current_player = player2
#print()
print(
"You have 0 health remaining and the "
"player2 has 1 health remaining."
.format(player1.health, player2.health))
#print()
if (current_player == player1):
print(movesprint)
move = get_selection()
else:
print(movesprint)
move = get_selection()
if (move == 1):
damage = random.randrange(12, 22)
if (current_player == player1):
player2.calculate_damage(damage, player1.name, player2.name)
else:
player1.calculate_damage(damage, player2.name, player1.name)
elif (move == 2):
damage = random.randrange(5, 35)
if (current_player == player1):
player2.calculate_damage(damage, player1.name, player2.name)
else:
player1.calculate_damage(damage, player2.name, player1.name)
elif (move == 3):
heal = random.randrange(15, 28)
current_player.calculate_heal(heal)
else:
print ("The input was not valid. Please select a choice again.")
if (player1.health == 0):
print("0 wins".format(player2.name))
player2.wins += 1
winner_name = player2.name
loser_name = player1.name
winner_path = 'accounts/0.json'.format(winner_name)
loser_path = 'accounts/0.json'.format(loser_name)
with open(winner_path, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(winner_path, 'w+') as g:
json.dump(data, g)
with open(loser_path, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(loser_path, 'w+') as g:
json.dump(data, g)
game_in_progress = False
if (player2.health == 0):
print("0 wins".format(player1.name))
player1.wins += 1
winner_name = player1.name
loser_name = player2.name
winner_path = 'accounts/0.json'.format(winner_name)
loser_path = 'accounts/0.json'.format(loser_name)
with open(winner_path, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(winner_path, 'w+') as g:
json.dump(data, g)
with open(loser_path, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(loser_path, 'w+') as g:
json.dump(data, g)
game_in_progress = False
def start_game_ai(name): #Play vs Ai, which you select your difficulty.
target = raw_input("Easy, med or hard?")
if target == "easy":
computer = Character("Computer_Easy") #Make sure you have made all of these accounts in folder Accounts
if target == "med": #Set stats however you like
computer = Character("Computer_Med")
if target == "hard":
computer = Character("Computer_Hard")
human = Character(name)
keep_playing = True
while (keep_playing is True):
print("Current Score:")
print("You - 0".format(human.wins))
print("Computer - 0".format(computer.wins))
computer.health = 100
human.health = 100
play_round_ai(computer, human)
print()
response = raw_input("Play another round?(Y/N)")
if (response.lower() == "n"):
break
def start_game_multiplayer(name, target): #Play a local 1v1 game.
player2 = Character(target)
player1 = Character(name)
keep_playing = True
while (keep_playing is True):
print("Current Score:")
print("Player1 - 0".format(player1.wins))
print("Player2 - 0".format(player2.wins))
player2.health = 100
player1.health = 100
play_round_multiplayer(player2, player1)
#print()
response = raw_input("Play another 1v1? (y/n)")
if (response == "y"):
continue
else:
break
def options(): #Main menu
name = raw_input("Username to load or create?")
print("What do you want to do? If this is your first time, type 5 to create your new account!")
move = raw_input("1. Battle AI | 2. List stats | 3. Play 1v1 | 4. Upgrade str/def | 5. Make new user")
if move == "1":
print("Make sure you already have made all Computer accounts! (Computer_Easy, Computer_Med, Computer_Hard)")
start_game_ai(name)
if move == "2":
stats = Character(name).load_account(name)
print stats
return
if move == "3":
target = raw_input("Who do you want to fight?")
start_game_multiplayer(name, target)
if move == "4":
Character(name).upgrade_stat(name)
stats = Character(name).load_account(name)
print stats
if move == "5":
Character(name).make_account(name)
else:
print("Exiting..")
return
options()
python python-2.7 battle-simulation
add a comment |Â
up vote
5
down vote
favorite
up vote
5
down vote
favorite
I have recently started to learn python, and I have made my first fighting duel game. I'm planning on adding more stuff in the future like shops and items so you get a way to spend gold, but for now the current features are:
Character creation (with saving and loading),
Upgrading Str/Def,- Keeping Score
Battling AI or Battling 1v1.- (you also get rewards for winning)
But for now I'm happy with my progress. Please review my code!
import random
import json
import time
r = random
#INSTRUCTIONS FOR FIRST TIME USE
#MAKE 3 COMPUTER ACCOUNTS NAMED "Computer_Easy", "Computer_Med" and "Computer_Hard" AND SET THEIR STATS HOWEVER YOU LIKE! OTHERWISE UNABLE TO PLAY VS AI
class Character():
#Change value of default if you want people to start with higher str/def when they make their account.
def __init__(self, name, default=1, health=None, strength=None,
defence=None, gold=0, exp=0, wins=0, losses=0,
):
self.name = name
self.health = 100
self.strength = default if strength is None else strength
self.defence = default if defence is None else defence
self.gold = 0
self.exp = 0
self.wins = 0
self.losses = 0
def make_account(self, name): #Create a new user to /accounts folder with default stats as listed above
save_name = name
path = 'accounts/0.json'.format(save_name)
data =
"health": self.health,
"strength": self.strength,
"defence": self.defence,
"gold": self.gold,
"exp": self.exp,
"wins": self.wins,
"losses": self.losses,
with open(path, 'w+') as f:
json.dump(data, f)
def load_account(self, name):
load_name = name
path_two = 'accounts/0.json'.format(load_name)
with open(path_two, 'r') as f:
data = json.load(f)
return data
def upgrade_stat(self, name): #Upgrade either defence or strength
target = raw_input("What stat do you want to upgrade?")
if target in "health", "gold", "exp", "wins", "losses":
print("you cannot do that!")
return
load_name = name
path_two = 'accounts/0.json'.format(load_name)
with open(path_two, 'r') as f:
data = json.load(f)
if data["exp"] < 100:
print("You don't have enough xp!")
else:
data["exp"] -= 100
data[target] += 1
with open (path_two, 'w+') as g:
json.dump(data, g)
def calculate_damage(self, damage_amount, attacker, defender):
attacker_stats = self.load_account(name=attacker)
defender_stats = self.load_account(name=defender)
#You get damage boost, depending on attacker strength level and defender defence level.
damage_amount_boost = (attacker_stats["strength"] * damage_amount / (attacker_stats["strength"] + defender_stats["defence"])) + damage_amount
if (damage_amount_boost > self.health):
overkill = abs(self.health - damage_amount_boost)
self.health = 0
if (overkill > 0):
print("0 takes fatal damage from 1, with 2 overkill!"
.format(self.name.capitalize(), attacker, overkill))
else:
print("0 takes fatal damage from 1!"
.format(self.name.capitalize(), attacker))
else:
self.health -= damage_amount_boost
print("0 takes 1 damage from 2!"
.format(self.name.capitalize(), damage_amount_boost, attacker))
def calculate_heal(self, heal_amount):
if (heal_amount + self.health > 100):
self.health = 100
print("0 heals back to full health!"
.format(self.name.capitalize()))
else:
self.health += heal_amount
print("0 heals for 1!"
.format(self.name.capitalize(), heal_amount))
def parse_int(input):
try:
int(input)
return True
except ValueError:
return False
def get_selection():
valid_input = False
while (valid_input is False):
print()
choice = input("Select an attack: ")
if (parse_int(choice) is True):
return int(choice)
else:
print("The input was invalid. Please try again.")
def get_computer_selection(health):
sleep_time = random.randrange(2, 5)
print("Computer thinking...")
time.sleep(sleep_time)
if (health <= 35):
# Have the computer heal ~50% of its turns when <= 35
result = random.randint(1, 6)
if (result % 2 == 0):
return 3
else:
return random.randint(1, 2)
elif (health == 100):
return random.randint(1, 2)
else:
return random.randint(1, 3)
def play_round_ai(computer, human):
game_in_progress = True
current_player = computer
movesprint = '1. Mild, 2. Hard, 3. Heal'
while game_in_progress:
if (current_player == computer):
current_player = human
else:
current_player = computer
print(
"You have 0 health remaining and the "
"computer has 1 health remaining."
.format(human.health, computer.health))
if (current_player == human):
print(movesprint)
move = get_selection()
else:
move = get_computer_selection(computer.health)
if (move == 1):
damage = random.randrange(12, 22)
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
elif (move == 2):
damage = random.randrange(5, 35)
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
elif (move == 3):
heal = random.randrange(15, 28)
current_player.calculate_heal(heal)
else:
print ("The input was not valid. Please select a choice again.")
if (human.health == 0):
print("Sorry, you lose!")
computer.wins += 1
load_names = human.name
paths = 'accounts/0.json'.format(load_names)
with open(paths, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(paths, 'w+') as g:
json.dump(data, g)
game_in_progress = False
if (computer.health == 0):
print("Congratulations, you beat the computer!")
human.wins += 1
load_names = human.name
paths = 'accounts/0.json'.format(load_names)
with open(paths, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(paths, 'w+') as g:
json.dump(data, g)
game_in_progress = False
def play_round_multiplayer(player2, player1):
game_in_progress = True
current_player = player2
movesprint = '1. Mild, 2. Hard, 3. Heal'
while game_in_progress:
if (current_player == player2):
current_player = player1
else:
current_player = player2
#print()
print(
"You have 0 health remaining and the "
"player2 has 1 health remaining."
.format(player1.health, player2.health))
#print()
if (current_player == player1):
print(movesprint)
move = get_selection()
else:
print(movesprint)
move = get_selection()
if (move == 1):
damage = random.randrange(12, 22)
if (current_player == player1):
player2.calculate_damage(damage, player1.name, player2.name)
else:
player1.calculate_damage(damage, player2.name, player1.name)
elif (move == 2):
damage = random.randrange(5, 35)
if (current_player == player1):
player2.calculate_damage(damage, player1.name, player2.name)
else:
player1.calculate_damage(damage, player2.name, player1.name)
elif (move == 3):
heal = random.randrange(15, 28)
current_player.calculate_heal(heal)
else:
print ("The input was not valid. Please select a choice again.")
if (player1.health == 0):
print("0 wins".format(player2.name))
player2.wins += 1
winner_name = player2.name
loser_name = player1.name
winner_path = 'accounts/0.json'.format(winner_name)
loser_path = 'accounts/0.json'.format(loser_name)
with open(winner_path, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(winner_path, 'w+') as g:
json.dump(data, g)
with open(loser_path, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(loser_path, 'w+') as g:
json.dump(data, g)
game_in_progress = False
if (player2.health == 0):
print("0 wins".format(player1.name))
player1.wins += 1
winner_name = player1.name
loser_name = player2.name
winner_path = 'accounts/0.json'.format(winner_name)
loser_path = 'accounts/0.json'.format(loser_name)
with open(winner_path, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(winner_path, 'w+') as g:
json.dump(data, g)
with open(loser_path, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(loser_path, 'w+') as g:
json.dump(data, g)
game_in_progress = False
def start_game_ai(name): #Play vs Ai, which you select your difficulty.
target = raw_input("Easy, med or hard?")
if target == "easy":
computer = Character("Computer_Easy") #Make sure you have made all of these accounts in folder Accounts
if target == "med": #Set stats however you like
computer = Character("Computer_Med")
if target == "hard":
computer = Character("Computer_Hard")
human = Character(name)
keep_playing = True
while (keep_playing is True):
print("Current Score:")
print("You - 0".format(human.wins))
print("Computer - 0".format(computer.wins))
computer.health = 100
human.health = 100
play_round_ai(computer, human)
print()
response = raw_input("Play another round?(Y/N)")
if (response.lower() == "n"):
break
def start_game_multiplayer(name, target): #Play a local 1v1 game.
player2 = Character(target)
player1 = Character(name)
keep_playing = True
while (keep_playing is True):
print("Current Score:")
print("Player1 - 0".format(player1.wins))
print("Player2 - 0".format(player2.wins))
player2.health = 100
player1.health = 100
play_round_multiplayer(player2, player1)
#print()
response = raw_input("Play another 1v1? (y/n)")
if (response == "y"):
continue
else:
break
def options(): #Main menu
name = raw_input("Username to load or create?")
print("What do you want to do? If this is your first time, type 5 to create your new account!")
move = raw_input("1. Battle AI | 2. List stats | 3. Play 1v1 | 4. Upgrade str/def | 5. Make new user")
if move == "1":
print("Make sure you already have made all Computer accounts! (Computer_Easy, Computer_Med, Computer_Hard)")
start_game_ai(name)
if move == "2":
stats = Character(name).load_account(name)
print stats
return
if move == "3":
target = raw_input("Who do you want to fight?")
start_game_multiplayer(name, target)
if move == "4":
Character(name).upgrade_stat(name)
stats = Character(name).load_account(name)
print stats
if move == "5":
Character(name).make_account(name)
else:
print("Exiting..")
return
options()
python python-2.7 battle-simulation
I have recently started to learn python, and I have made my first fighting duel game. I'm planning on adding more stuff in the future like shops and items so you get a way to spend gold, but for now the current features are:
Character creation (with saving and loading),
Upgrading Str/Def,- Keeping Score
Battling AI or Battling 1v1.- (you also get rewards for winning)
But for now I'm happy with my progress. Please review my code!
import random
import json
import time
r = random
#INSTRUCTIONS FOR FIRST TIME USE
#MAKE 3 COMPUTER ACCOUNTS NAMED "Computer_Easy", "Computer_Med" and "Computer_Hard" AND SET THEIR STATS HOWEVER YOU LIKE! OTHERWISE UNABLE TO PLAY VS AI
class Character():
#Change value of default if you want people to start with higher str/def when they make their account.
def __init__(self, name, default=1, health=None, strength=None,
defence=None, gold=0, exp=0, wins=0, losses=0,
):
self.name = name
self.health = 100
self.strength = default if strength is None else strength
self.defence = default if defence is None else defence
self.gold = 0
self.exp = 0
self.wins = 0
self.losses = 0
def make_account(self, name): #Create a new user to /accounts folder with default stats as listed above
save_name = name
path = 'accounts/0.json'.format(save_name)
data =
"health": self.health,
"strength": self.strength,
"defence": self.defence,
"gold": self.gold,
"exp": self.exp,
"wins": self.wins,
"losses": self.losses,
with open(path, 'w+') as f:
json.dump(data, f)
def load_account(self, name):
load_name = name
path_two = 'accounts/0.json'.format(load_name)
with open(path_two, 'r') as f:
data = json.load(f)
return data
def upgrade_stat(self, name): #Upgrade either defence or strength
target = raw_input("What stat do you want to upgrade?")
if target in "health", "gold", "exp", "wins", "losses":
print("you cannot do that!")
return
load_name = name
path_two = 'accounts/0.json'.format(load_name)
with open(path_two, 'r') as f:
data = json.load(f)
if data["exp"] < 100:
print("You don't have enough xp!")
else:
data["exp"] -= 100
data[target] += 1
with open (path_two, 'w+') as g:
json.dump(data, g)
def calculate_damage(self, damage_amount, attacker, defender):
attacker_stats = self.load_account(name=attacker)
defender_stats = self.load_account(name=defender)
#You get damage boost, depending on attacker strength level and defender defence level.
damage_amount_boost = (attacker_stats["strength"] * damage_amount / (attacker_stats["strength"] + defender_stats["defence"])) + damage_amount
if (damage_amount_boost > self.health):
overkill = abs(self.health - damage_amount_boost)
self.health = 0
if (overkill > 0):
print("0 takes fatal damage from 1, with 2 overkill!"
.format(self.name.capitalize(), attacker, overkill))
else:
print("0 takes fatal damage from 1!"
.format(self.name.capitalize(), attacker))
else:
self.health -= damage_amount_boost
print("0 takes 1 damage from 2!"
.format(self.name.capitalize(), damage_amount_boost, attacker))
def calculate_heal(self, heal_amount):
if (heal_amount + self.health > 100):
self.health = 100
print("0 heals back to full health!"
.format(self.name.capitalize()))
else:
self.health += heal_amount
print("0 heals for 1!"
.format(self.name.capitalize(), heal_amount))
def parse_int(input):
try:
int(input)
return True
except ValueError:
return False
def get_selection():
valid_input = False
while (valid_input is False):
print()
choice = input("Select an attack: ")
if (parse_int(choice) is True):
return int(choice)
else:
print("The input was invalid. Please try again.")
def get_computer_selection(health):
sleep_time = random.randrange(2, 5)
print("Computer thinking...")
time.sleep(sleep_time)
if (health <= 35):
# Have the computer heal ~50% of its turns when <= 35
result = random.randint(1, 6)
if (result % 2 == 0):
return 3
else:
return random.randint(1, 2)
elif (health == 100):
return random.randint(1, 2)
else:
return random.randint(1, 3)
def play_round_ai(computer, human):
game_in_progress = True
current_player = computer
movesprint = '1. Mild, 2. Hard, 3. Heal'
while game_in_progress:
if (current_player == computer):
current_player = human
else:
current_player = computer
print(
"You have 0 health remaining and the "
"computer has 1 health remaining."
.format(human.health, computer.health))
if (current_player == human):
print(movesprint)
move = get_selection()
else:
move = get_computer_selection(computer.health)
if (move == 1):
damage = random.randrange(12, 22)
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
elif (move == 2):
damage = random.randrange(5, 35)
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
elif (move == 3):
heal = random.randrange(15, 28)
current_player.calculate_heal(heal)
else:
print ("The input was not valid. Please select a choice again.")
if (human.health == 0):
print("Sorry, you lose!")
computer.wins += 1
load_names = human.name
paths = 'accounts/0.json'.format(load_names)
with open(paths, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(paths, 'w+') as g:
json.dump(data, g)
game_in_progress = False
if (computer.health == 0):
print("Congratulations, you beat the computer!")
human.wins += 1
load_names = human.name
paths = 'accounts/0.json'.format(load_names)
with open(paths, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(paths, 'w+') as g:
json.dump(data, g)
game_in_progress = False
def play_round_multiplayer(player2, player1):
game_in_progress = True
current_player = player2
movesprint = '1. Mild, 2. Hard, 3. Heal'
while game_in_progress:
if (current_player == player2):
current_player = player1
else:
current_player = player2
#print()
print(
"You have 0 health remaining and the "
"player2 has 1 health remaining."
.format(player1.health, player2.health))
#print()
if (current_player == player1):
print(movesprint)
move = get_selection()
else:
print(movesprint)
move = get_selection()
if (move == 1):
damage = random.randrange(12, 22)
if (current_player == player1):
player2.calculate_damage(damage, player1.name, player2.name)
else:
player1.calculate_damage(damage, player2.name, player1.name)
elif (move == 2):
damage = random.randrange(5, 35)
if (current_player == player1):
player2.calculate_damage(damage, player1.name, player2.name)
else:
player1.calculate_damage(damage, player2.name, player1.name)
elif (move == 3):
heal = random.randrange(15, 28)
current_player.calculate_heal(heal)
else:
print ("The input was not valid. Please select a choice again.")
if (player1.health == 0):
print("0 wins".format(player2.name))
player2.wins += 1
winner_name = player2.name
loser_name = player1.name
winner_path = 'accounts/0.json'.format(winner_name)
loser_path = 'accounts/0.json'.format(loser_name)
with open(winner_path, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(winner_path, 'w+') as g:
json.dump(data, g)
with open(loser_path, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(loser_path, 'w+') as g:
json.dump(data, g)
game_in_progress = False
if (player2.health == 0):
print("0 wins".format(player1.name))
player1.wins += 1
winner_name = player1.name
loser_name = player2.name
winner_path = 'accounts/0.json'.format(winner_name)
loser_path = 'accounts/0.json'.format(loser_name)
with open(winner_path, 'r') as f:
data = json.load(f)
data["exp"] += 25
data["gold"] += 100
data["wins"] += 1
with open(winner_path, 'w+') as g:
json.dump(data, g)
with open(loser_path, 'r') as f:
data = json.load(f)
data["losses"] += 1
with open(loser_path, 'w+') as g:
json.dump(data, g)
game_in_progress = False
def start_game_ai(name): #Play vs Ai, which you select your difficulty.
target = raw_input("Easy, med or hard?")
if target == "easy":
computer = Character("Computer_Easy") #Make sure you have made all of these accounts in folder Accounts
if target == "med": #Set stats however you like
computer = Character("Computer_Med")
if target == "hard":
computer = Character("Computer_Hard")
human = Character(name)
keep_playing = True
while (keep_playing is True):
print("Current Score:")
print("You - 0".format(human.wins))
print("Computer - 0".format(computer.wins))
computer.health = 100
human.health = 100
play_round_ai(computer, human)
print()
response = raw_input("Play another round?(Y/N)")
if (response.lower() == "n"):
break
def start_game_multiplayer(name, target): #Play a local 1v1 game.
player2 = Character(target)
player1 = Character(name)
keep_playing = True
while (keep_playing is True):
print("Current Score:")
print("Player1 - 0".format(player1.wins))
print("Player2 - 0".format(player2.wins))
player2.health = 100
player1.health = 100
play_round_multiplayer(player2, player1)
#print()
response = raw_input("Play another 1v1? (y/n)")
if (response == "y"):
continue
else:
break
def options(): #Main menu
name = raw_input("Username to load or create?")
print("What do you want to do? If this is your first time, type 5 to create your new account!")
move = raw_input("1. Battle AI | 2. List stats | 3. Play 1v1 | 4. Upgrade str/def | 5. Make new user")
if move == "1":
print("Make sure you already have made all Computer accounts! (Computer_Easy, Computer_Med, Computer_Hard)")
start_game_ai(name)
if move == "2":
stats = Character(name).load_account(name)
print stats
return
if move == "3":
target = raw_input("Who do you want to fight?")
start_game_multiplayer(name, target)
if move == "4":
Character(name).upgrade_stat(name)
stats = Character(name).load_account(name)
print stats
if move == "5":
Character(name).make_account(name)
else:
print("Exiting..")
return
options()
python python-2.7 battle-simulation
edited Jul 11 at 17:44
200_success
123k14143399
123k14143399
asked Jul 11 at 14:56
VissyHunter
263
263
add a comment |Â
add a comment |Â
3 Answers
3
active
oldest
votes
up vote
5
down vote
Currently your code is a bit all over the place. You have a Character
class that can keep track of how much e.g. exp a character has. But when it comes to adding exp, you instead use a data
dictionary.
Also, instead of saving the Character
to file, you save that dictionary instead. The only thing where you actually use that class is to get the characters name.
So you have two options:
- Get rid of the class and always use the
data
dictionary. - Make all of those things something the
class
handles.
I would choose option 2 here.
Here is a start on that road:
import random
import json
import time
r = random
# INSTRUCTIONS FOR FIRST TIME USE
# MAKE 3 COMPUTER ACCOUNTS NAMED "Computer_Easy", "Computer_Med" and
# "Computer_Hard" AND SET THEIR STATS HOWEVER YOU LIKE! OTHERWISE UNABLE
# TO PLAY VS AI
class CharacterDoesNotExist(Exception):
pass
class Character(object):
# Change value of default if you want people to start with higher str/def
# when they make their account.
path = 'accounts/.json'
mild_attack = 12, 22
hard_attack = 5, 35
heal_amount = 15, 28
default = 1
def __init__(self, name, health=None, strength=None,
defence=None, gold=0, exp=0, wins=0, losses=0):
self.name = name
self.health = 100 if health is None else health
self.strength = self.default if strength is None else strength
self.defence = self.default if defence is None else defence
self.gold = 0 if gold is None else gold
self.exp = 0 if exp is None else exp
self.wins = 0 if wins is None else wins
self.losses = 0 if losses is None else wins
@classmethod
def load(cls, name):
path = cls.path.format(name)
try:
with open(path) as f:
return cls(**json.load(f))
except Exception:
raise CharacterDoesNotExist(name)
def save(self):
path = self.path.format(self.name)
with open(path, 'w+') as f:
json.dump(self.__dict__, f)
def __str__(self):
return self.name.capitalize()
def upgrade_stat(self): # Upgrade either defence or strength
if self.exp < 100:
print("You don't have enough exp!")
return
target = raw_input("What stat do you want to upgrade?")
if target not in ("strength", "defence"):
print("You cannot do that!")
else:
self.exp -= 100
setattr(self, target, getattr(self, target) + 1)
self.save()
def attack(self, defender, damage_amount):
# You get damage boost, depending on attacker strength level and
# defender defence level.
damage_amount_boost = (self.strength * damage_amount /
(self.strength + defender.defence)) + damage_amount
if damage_amount_boost > defender.health:
overkill = abs(self.health - damage_amount_boost)
defender.health = 0
if overkill > 0:
print(" takes fatal damage from , with overkill!".format(
defender, self, overkill))
else:
print(" takes fatal damage from !".format(defender, self))
else:
defender.health -= damage_amount_boost
print(" takes damage from !".format(
defender, damage_amount_boost, self))
def heal(self, heal_amount):
if (heal_amount + self.health > 100):
self.health = 100
print(" heals back to full health!".format(self))
else:
self.health += heal_amount
print(" heals for !".format(self, heal_amount))
def get_selection(self):
print 'Avalaible moves: 1. Mild Attack, 2. Hard Attack, 3. Heal'
while True:
try:
choice = int(input("Select a move: "))
if not 1 <= choice <= 3:
continue
return choice
except ValueError:
print("The input was invalid. Please try again.")
def make_move(self, player2):
choice = self.get_selection()
if choice == 1:
# mild attack
damage = random.randint(*self.mild_attack)
self.attack(player2, damage)
elif choice == 2:
# mild attack
damage = random.randint(*self.hard_attack)
self.attack(player2, damage)
elif choice == 3:
# mild attack
heal = random.randint(*self.heal_amount)
self.heal(heal)
def win(self):
self.wins += 1
self.exp += 25
self.gold += 100
self.save()
def loose(self):
self.losses += 1
self.save()
class Computer(Character):
def get_selection(self):
print("Computer thinking...")
time.sleep(random.randrange(2, 5))
if self.health <= 35:
# Have the computer heal ~50% of its turns when <= 35
if random.random() >= 0.5:
return 3
return random.randint(1, 2)
elif self.health == 100:
return random.randint(1, 2)
return random.randint(1, 3)
def win(self):
self.wins += 1
def loose(self):
self.losses += 1
def play_round(player1, player2):
player1.health = player2.health = 100
while True:
print("n has health remaining and has health remaining.".format(
player1, player1.health, player2, player2.health))
for _ in range(2):
player1.make_move(player2)
if player2.health <= 0:
print(" looses!".format(player2))
player1.win()
player2.loose()
return
# swap the players
player1, player2 = player2, player1
# Play vs Ai, which you select your difficulty.
def play_vs_ai(human, computer):
while True:
print "Current Score:"
print "You - ".format(human.wins)
print "Computer - ".format(computer.wins)
play_round(computer, human)
print
if raw_input("Play another round?(Y/N)").lower() == "n":
break
def play_1_vs_1(player1, player2): # Play a local 1v1 game.
while True:
print "Current Score:"
print "Player1 - ".format(player1.wins)
print "Player2 - ".format(player2.wins)
play_round(player1, player1)
print
if raw_input("Play another 1v1? (y/n)").lower() != "y":
break
def main_menu(): # Main menu
name = raw_input("Username to load or create?")
try:
player1 = Character.load(name)
except CharacterDoesNotExist:
player1 = Character(name)
player1.save()
while True:
print("What do you want to do?")
move = raw_input(
"1. Battle AI | 2. List stats | 3. Play 1v1 | 4. Upgrade str/def")
if move == "1":
target = raw_input("Easy, med or hard?")
if target.lower() == "easy":
computer = Computer("Computer_Easy")
elif target.lower() == "med": # Set stats however you like
computer = Computer("Computer_Med", strength=2, defence=3)
elif target.lower() == "hard":
computer = Computer("Computer_Hard", strength=5, defence=4)
play_vs_ai(player1, computer)
elif move == "2":
print player1.__dict__
elif move == "3":
target = raw_input("Who do you want to fight?")
try:
player2 = Character.load(target)
except CharacterDoesNotExist:
player2 = Character(target)
player2.save()
play_1_vs_1(player1, player2)
elif move == "4":
while True:
player = raw_input("Which player: or ")
if player == player1.name:
player1.upgrade_stat()
break
# elif player == player2.name:
# player2.upgrade_stat()
# break
else:
print("Exiting..")
return
if __name__ == "__main__":
main_menu()
Some notable changes:
- The
Character
class inherits fromobject
, making it a new-style class. - This uses a
Computer
class that inherits fromCharacter
and overrides some methods to allow the computer to make its decisions and prevent it from saving its wins and losses. - It has a
classmethod
load
that loads a character from a file and asave
method to save it. - The
__str__
magic method makes sure that when we print a character that we see their name - Instead of disallowing some stats (like wins) to be upgraded, explicitly allow only strength and defence to be upgraded (whitelist vs blacklist).
- A
Character
canattack
andheal
. By how much is encoded as a class variable. - Some of the attributes can be reached like for a
dict
, to ease the upgrading of a stat. - There is a
make_move
function that performs a characters move, given a choice of moves. - The
play_round
function is generic enough that it does not care if both players are humans or even both are computers. - The only thing that does not currently work is upgrading the stats of player 2, if they are human.
- You don't need to specify the position in
str.format
strings, if you use them in the order you pass it in. In other words"0 1".format(1, 2)
and" ".format(1, 2)
are equivalent. - It uses a
if __name__ == "__main__":
guard to allow importing from this module. - A new character is automatically created if the loading fails.
- The AI players have default values now. Whether or not they make sense, I don't know (I managed to consistently beat the easy AI...).
- Consistently use
print
as a statement, as it was in Python 2.
Some things which you should still change:
- Make all those comments above your functions into
docstrings
. - Port this to Python 3, as Python 2 will be EOL soon.
2
Instead of printing a__dict__
prefer usingvars
. Also, if defining__getitem__
and__setitem__
is only useful forself[target] += 1
inupgrade_stat
you shoul favorgetattr
and/orhasattr
instead and handle a failure the same as a restricted attribute. Besides, you may want to check for the XP amount before asking what to upgrade.
â Mathias Ettinger
Jul 12 at 7:12
@MathiasEttinger: Agree, when I was writing it I thought I might need to set and get more attributes. Removed those methods entirely and replaced them with a call tosetattr
andgetattr
. Btw, I am checking that the user has enough exp.
â Graipher
Jul 12 at 8:57
1
But the check comes after asking the question ;)
â Mathias Ettinger
Jul 12 at 9:00
add a comment |Â
up vote
4
down vote
parse_int()
has no reason to be a function, as it doesn't simplify anything.get_selection()
could look like this:def get_selection():
valid_input = False
while (valid_input is False):
print()
choice = input("Select an attack: ")
try:
return int(choice)
except ValueError:
print("The input was invalid. Please try again.")For comparing object identity (i.e.
current_player == player2
orcurrent_player == computer
) useis
. this checks if the objects are the same, rather than if they have the same values. In your case, because you did not override==
they should currently act the same.You can abstract out a single human or computer turn as a function, so that the different combinations of human computer play are all the same code. This will also make it easier to do AI vs. AI duels.
Try to separate the game code from the output code. What if you or someone else wanted to make a different user interface for your game? First try to remove printing from the
Character
class, then try to make a quiet turn function that handles dueling using arguments and return values.Instead of
r = random
you can doimport random as r
. In my opinion however, it is not a good idea to change the name of a module as it makes it harder for readers to figure out which module you are using.
add a comment |Â
up vote
2
down vote
Try to eliminate repetitions. For example, you have a lot of âÂÂgenerate filename, open, read/writeâ statements. Instead, create a separate class which could use along the lines of
storage = FileStorage(name) # reads "accounts/name.json" into storage.data
storage.data["something"] = whatever
storage.save()
Where else are you repeating yourself a lot? You have
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
But you can simplify this to
active_player, non_active_player = human, computer
...
active_player.calculate_damage(damage, active_player.name, non_active_player.name)
...
# and when the turn ends:
active_player, non_active_player = non_active_player, active_player
And so on.
Repetition is bad because whenever you want to make a change you have to do it in multiple places, and you risk failing to update all the places or updating some of them differently from others.
add a comment |Â
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
5
down vote
Currently your code is a bit all over the place. You have a Character
class that can keep track of how much e.g. exp a character has. But when it comes to adding exp, you instead use a data
dictionary.
Also, instead of saving the Character
to file, you save that dictionary instead. The only thing where you actually use that class is to get the characters name.
So you have two options:
- Get rid of the class and always use the
data
dictionary. - Make all of those things something the
class
handles.
I would choose option 2 here.
Here is a start on that road:
import random
import json
import time
r = random
# INSTRUCTIONS FOR FIRST TIME USE
# MAKE 3 COMPUTER ACCOUNTS NAMED "Computer_Easy", "Computer_Med" and
# "Computer_Hard" AND SET THEIR STATS HOWEVER YOU LIKE! OTHERWISE UNABLE
# TO PLAY VS AI
class CharacterDoesNotExist(Exception):
pass
class Character(object):
# Change value of default if you want people to start with higher str/def
# when they make their account.
path = 'accounts/.json'
mild_attack = 12, 22
hard_attack = 5, 35
heal_amount = 15, 28
default = 1
def __init__(self, name, health=None, strength=None,
defence=None, gold=0, exp=0, wins=0, losses=0):
self.name = name
self.health = 100 if health is None else health
self.strength = self.default if strength is None else strength
self.defence = self.default if defence is None else defence
self.gold = 0 if gold is None else gold
self.exp = 0 if exp is None else exp
self.wins = 0 if wins is None else wins
self.losses = 0 if losses is None else wins
@classmethod
def load(cls, name):
path = cls.path.format(name)
try:
with open(path) as f:
return cls(**json.load(f))
except Exception:
raise CharacterDoesNotExist(name)
def save(self):
path = self.path.format(self.name)
with open(path, 'w+') as f:
json.dump(self.__dict__, f)
def __str__(self):
return self.name.capitalize()
def upgrade_stat(self): # Upgrade either defence or strength
if self.exp < 100:
print("You don't have enough exp!")
return
target = raw_input("What stat do you want to upgrade?")
if target not in ("strength", "defence"):
print("You cannot do that!")
else:
self.exp -= 100
setattr(self, target, getattr(self, target) + 1)
self.save()
def attack(self, defender, damage_amount):
# You get damage boost, depending on attacker strength level and
# defender defence level.
damage_amount_boost = (self.strength * damage_amount /
(self.strength + defender.defence)) + damage_amount
if damage_amount_boost > defender.health:
overkill = abs(self.health - damage_amount_boost)
defender.health = 0
if overkill > 0:
print(" takes fatal damage from , with overkill!".format(
defender, self, overkill))
else:
print(" takes fatal damage from !".format(defender, self))
else:
defender.health -= damage_amount_boost
print(" takes damage from !".format(
defender, damage_amount_boost, self))
def heal(self, heal_amount):
if (heal_amount + self.health > 100):
self.health = 100
print(" heals back to full health!".format(self))
else:
self.health += heal_amount
print(" heals for !".format(self, heal_amount))
def get_selection(self):
print 'Avalaible moves: 1. Mild Attack, 2. Hard Attack, 3. Heal'
while True:
try:
choice = int(input("Select a move: "))
if not 1 <= choice <= 3:
continue
return choice
except ValueError:
print("The input was invalid. Please try again.")
def make_move(self, player2):
choice = self.get_selection()
if choice == 1:
# mild attack
damage = random.randint(*self.mild_attack)
self.attack(player2, damage)
elif choice == 2:
# mild attack
damage = random.randint(*self.hard_attack)
self.attack(player2, damage)
elif choice == 3:
# mild attack
heal = random.randint(*self.heal_amount)
self.heal(heal)
def win(self):
self.wins += 1
self.exp += 25
self.gold += 100
self.save()
def loose(self):
self.losses += 1
self.save()
class Computer(Character):
def get_selection(self):
print("Computer thinking...")
time.sleep(random.randrange(2, 5))
if self.health <= 35:
# Have the computer heal ~50% of its turns when <= 35
if random.random() >= 0.5:
return 3
return random.randint(1, 2)
elif self.health == 100:
return random.randint(1, 2)
return random.randint(1, 3)
def win(self):
self.wins += 1
def loose(self):
self.losses += 1
def play_round(player1, player2):
player1.health = player2.health = 100
while True:
print("n has health remaining and has health remaining.".format(
player1, player1.health, player2, player2.health))
for _ in range(2):
player1.make_move(player2)
if player2.health <= 0:
print(" looses!".format(player2))
player1.win()
player2.loose()
return
# swap the players
player1, player2 = player2, player1
# Play vs Ai, which you select your difficulty.
def play_vs_ai(human, computer):
while True:
print "Current Score:"
print "You - ".format(human.wins)
print "Computer - ".format(computer.wins)
play_round(computer, human)
print
if raw_input("Play another round?(Y/N)").lower() == "n":
break
def play_1_vs_1(player1, player2): # Play a local 1v1 game.
while True:
print "Current Score:"
print "Player1 - ".format(player1.wins)
print "Player2 - ".format(player2.wins)
play_round(player1, player1)
print
if raw_input("Play another 1v1? (y/n)").lower() != "y":
break
def main_menu(): # Main menu
name = raw_input("Username to load or create?")
try:
player1 = Character.load(name)
except CharacterDoesNotExist:
player1 = Character(name)
player1.save()
while True:
print("What do you want to do?")
move = raw_input(
"1. Battle AI | 2. List stats | 3. Play 1v1 | 4. Upgrade str/def")
if move == "1":
target = raw_input("Easy, med or hard?")
if target.lower() == "easy":
computer = Computer("Computer_Easy")
elif target.lower() == "med": # Set stats however you like
computer = Computer("Computer_Med", strength=2, defence=3)
elif target.lower() == "hard":
computer = Computer("Computer_Hard", strength=5, defence=4)
play_vs_ai(player1, computer)
elif move == "2":
print player1.__dict__
elif move == "3":
target = raw_input("Who do you want to fight?")
try:
player2 = Character.load(target)
except CharacterDoesNotExist:
player2 = Character(target)
player2.save()
play_1_vs_1(player1, player2)
elif move == "4":
while True:
player = raw_input("Which player: or ")
if player == player1.name:
player1.upgrade_stat()
break
# elif player == player2.name:
# player2.upgrade_stat()
# break
else:
print("Exiting..")
return
if __name__ == "__main__":
main_menu()
Some notable changes:
- The
Character
class inherits fromobject
, making it a new-style class. - This uses a
Computer
class that inherits fromCharacter
and overrides some methods to allow the computer to make its decisions and prevent it from saving its wins and losses. - It has a
classmethod
load
that loads a character from a file and asave
method to save it. - The
__str__
magic method makes sure that when we print a character that we see their name - Instead of disallowing some stats (like wins) to be upgraded, explicitly allow only strength and defence to be upgraded (whitelist vs blacklist).
- A
Character
canattack
andheal
. By how much is encoded as a class variable. - Some of the attributes can be reached like for a
dict
, to ease the upgrading of a stat. - There is a
make_move
function that performs a characters move, given a choice of moves. - The
play_round
function is generic enough that it does not care if both players are humans or even both are computers. - The only thing that does not currently work is upgrading the stats of player 2, if they are human.
- You don't need to specify the position in
str.format
strings, if you use them in the order you pass it in. In other words"0 1".format(1, 2)
and" ".format(1, 2)
are equivalent. - It uses a
if __name__ == "__main__":
guard to allow importing from this module. - A new character is automatically created if the loading fails.
- The AI players have default values now. Whether or not they make sense, I don't know (I managed to consistently beat the easy AI...).
- Consistently use
print
as a statement, as it was in Python 2.
Some things which you should still change:
- Make all those comments above your functions into
docstrings
. - Port this to Python 3, as Python 2 will be EOL soon.
2
Instead of printing a__dict__
prefer usingvars
. Also, if defining__getitem__
and__setitem__
is only useful forself[target] += 1
inupgrade_stat
you shoul favorgetattr
and/orhasattr
instead and handle a failure the same as a restricted attribute. Besides, you may want to check for the XP amount before asking what to upgrade.
â Mathias Ettinger
Jul 12 at 7:12
@MathiasEttinger: Agree, when I was writing it I thought I might need to set and get more attributes. Removed those methods entirely and replaced them with a call tosetattr
andgetattr
. Btw, I am checking that the user has enough exp.
â Graipher
Jul 12 at 8:57
1
But the check comes after asking the question ;)
â Mathias Ettinger
Jul 12 at 9:00
add a comment |Â
up vote
5
down vote
Currently your code is a bit all over the place. You have a Character
class that can keep track of how much e.g. exp a character has. But when it comes to adding exp, you instead use a data
dictionary.
Also, instead of saving the Character
to file, you save that dictionary instead. The only thing where you actually use that class is to get the characters name.
So you have two options:
- Get rid of the class and always use the
data
dictionary. - Make all of those things something the
class
handles.
I would choose option 2 here.
Here is a start on that road:
import random
import json
import time
r = random
# INSTRUCTIONS FOR FIRST TIME USE
# MAKE 3 COMPUTER ACCOUNTS NAMED "Computer_Easy", "Computer_Med" and
# "Computer_Hard" AND SET THEIR STATS HOWEVER YOU LIKE! OTHERWISE UNABLE
# TO PLAY VS AI
class CharacterDoesNotExist(Exception):
pass
class Character(object):
# Change value of default if you want people to start with higher str/def
# when they make their account.
path = 'accounts/.json'
mild_attack = 12, 22
hard_attack = 5, 35
heal_amount = 15, 28
default = 1
def __init__(self, name, health=None, strength=None,
defence=None, gold=0, exp=0, wins=0, losses=0):
self.name = name
self.health = 100 if health is None else health
self.strength = self.default if strength is None else strength
self.defence = self.default if defence is None else defence
self.gold = 0 if gold is None else gold
self.exp = 0 if exp is None else exp
self.wins = 0 if wins is None else wins
self.losses = 0 if losses is None else wins
@classmethod
def load(cls, name):
path = cls.path.format(name)
try:
with open(path) as f:
return cls(**json.load(f))
except Exception:
raise CharacterDoesNotExist(name)
def save(self):
path = self.path.format(self.name)
with open(path, 'w+') as f:
json.dump(self.__dict__, f)
def __str__(self):
return self.name.capitalize()
def upgrade_stat(self): # Upgrade either defence or strength
if self.exp < 100:
print("You don't have enough exp!")
return
target = raw_input("What stat do you want to upgrade?")
if target not in ("strength", "defence"):
print("You cannot do that!")
else:
self.exp -= 100
setattr(self, target, getattr(self, target) + 1)
self.save()
def attack(self, defender, damage_amount):
# You get damage boost, depending on attacker strength level and
# defender defence level.
damage_amount_boost = (self.strength * damage_amount /
(self.strength + defender.defence)) + damage_amount
if damage_amount_boost > defender.health:
overkill = abs(self.health - damage_amount_boost)
defender.health = 0
if overkill > 0:
print(" takes fatal damage from , with overkill!".format(
defender, self, overkill))
else:
print(" takes fatal damage from !".format(defender, self))
else:
defender.health -= damage_amount_boost
print(" takes damage from !".format(
defender, damage_amount_boost, self))
def heal(self, heal_amount):
if (heal_amount + self.health > 100):
self.health = 100
print(" heals back to full health!".format(self))
else:
self.health += heal_amount
print(" heals for !".format(self, heal_amount))
def get_selection(self):
print 'Avalaible moves: 1. Mild Attack, 2. Hard Attack, 3. Heal'
while True:
try:
choice = int(input("Select a move: "))
if not 1 <= choice <= 3:
continue
return choice
except ValueError:
print("The input was invalid. Please try again.")
def make_move(self, player2):
choice = self.get_selection()
if choice == 1:
# mild attack
damage = random.randint(*self.mild_attack)
self.attack(player2, damage)
elif choice == 2:
# mild attack
damage = random.randint(*self.hard_attack)
self.attack(player2, damage)
elif choice == 3:
# mild attack
heal = random.randint(*self.heal_amount)
self.heal(heal)
def win(self):
self.wins += 1
self.exp += 25
self.gold += 100
self.save()
def loose(self):
self.losses += 1
self.save()
class Computer(Character):
def get_selection(self):
print("Computer thinking...")
time.sleep(random.randrange(2, 5))
if self.health <= 35:
# Have the computer heal ~50% of its turns when <= 35
if random.random() >= 0.5:
return 3
return random.randint(1, 2)
elif self.health == 100:
return random.randint(1, 2)
return random.randint(1, 3)
def win(self):
self.wins += 1
def loose(self):
self.losses += 1
def play_round(player1, player2):
player1.health = player2.health = 100
while True:
print("n has health remaining and has health remaining.".format(
player1, player1.health, player2, player2.health))
for _ in range(2):
player1.make_move(player2)
if player2.health <= 0:
print(" looses!".format(player2))
player1.win()
player2.loose()
return
# swap the players
player1, player2 = player2, player1
# Play vs Ai, which you select your difficulty.
def play_vs_ai(human, computer):
while True:
print "Current Score:"
print "You - ".format(human.wins)
print "Computer - ".format(computer.wins)
play_round(computer, human)
print
if raw_input("Play another round?(Y/N)").lower() == "n":
break
def play_1_vs_1(player1, player2): # Play a local 1v1 game.
while True:
print "Current Score:"
print "Player1 - ".format(player1.wins)
print "Player2 - ".format(player2.wins)
play_round(player1, player1)
print
if raw_input("Play another 1v1? (y/n)").lower() != "y":
break
def main_menu(): # Main menu
name = raw_input("Username to load or create?")
try:
player1 = Character.load(name)
except CharacterDoesNotExist:
player1 = Character(name)
player1.save()
while True:
print("What do you want to do?")
move = raw_input(
"1. Battle AI | 2. List stats | 3. Play 1v1 | 4. Upgrade str/def")
if move == "1":
target = raw_input("Easy, med or hard?")
if target.lower() == "easy":
computer = Computer("Computer_Easy")
elif target.lower() == "med": # Set stats however you like
computer = Computer("Computer_Med", strength=2, defence=3)
elif target.lower() == "hard":
computer = Computer("Computer_Hard", strength=5, defence=4)
play_vs_ai(player1, computer)
elif move == "2":
print player1.__dict__
elif move == "3":
target = raw_input("Who do you want to fight?")
try:
player2 = Character.load(target)
except CharacterDoesNotExist:
player2 = Character(target)
player2.save()
play_1_vs_1(player1, player2)
elif move == "4":
while True:
player = raw_input("Which player: or ")
if player == player1.name:
player1.upgrade_stat()
break
# elif player == player2.name:
# player2.upgrade_stat()
# break
else:
print("Exiting..")
return
if __name__ == "__main__":
main_menu()
Some notable changes:
- The
Character
class inherits fromobject
, making it a new-style class. - This uses a
Computer
class that inherits fromCharacter
and overrides some methods to allow the computer to make its decisions and prevent it from saving its wins and losses. - It has a
classmethod
load
that loads a character from a file and asave
method to save it. - The
__str__
magic method makes sure that when we print a character that we see their name - Instead of disallowing some stats (like wins) to be upgraded, explicitly allow only strength and defence to be upgraded (whitelist vs blacklist).
- A
Character
canattack
andheal
. By how much is encoded as a class variable. - Some of the attributes can be reached like for a
dict
, to ease the upgrading of a stat. - There is a
make_move
function that performs a characters move, given a choice of moves. - The
play_round
function is generic enough that it does not care if both players are humans or even both are computers. - The only thing that does not currently work is upgrading the stats of player 2, if they are human.
- You don't need to specify the position in
str.format
strings, if you use them in the order you pass it in. In other words"0 1".format(1, 2)
and" ".format(1, 2)
are equivalent. - It uses a
if __name__ == "__main__":
guard to allow importing from this module. - A new character is automatically created if the loading fails.
- The AI players have default values now. Whether or not they make sense, I don't know (I managed to consistently beat the easy AI...).
- Consistently use
print
as a statement, as it was in Python 2.
Some things which you should still change:
- Make all those comments above your functions into
docstrings
. - Port this to Python 3, as Python 2 will be EOL soon.
2
Instead of printing a__dict__
prefer usingvars
. Also, if defining__getitem__
and__setitem__
is only useful forself[target] += 1
inupgrade_stat
you shoul favorgetattr
and/orhasattr
instead and handle a failure the same as a restricted attribute. Besides, you may want to check for the XP amount before asking what to upgrade.
â Mathias Ettinger
Jul 12 at 7:12
@MathiasEttinger: Agree, when I was writing it I thought I might need to set and get more attributes. Removed those methods entirely and replaced them with a call tosetattr
andgetattr
. Btw, I am checking that the user has enough exp.
â Graipher
Jul 12 at 8:57
1
But the check comes after asking the question ;)
â Mathias Ettinger
Jul 12 at 9:00
add a comment |Â
up vote
5
down vote
up vote
5
down vote
Currently your code is a bit all over the place. You have a Character
class that can keep track of how much e.g. exp a character has. But when it comes to adding exp, you instead use a data
dictionary.
Also, instead of saving the Character
to file, you save that dictionary instead. The only thing where you actually use that class is to get the characters name.
So you have two options:
- Get rid of the class and always use the
data
dictionary. - Make all of those things something the
class
handles.
I would choose option 2 here.
Here is a start on that road:
import random
import json
import time
r = random
# INSTRUCTIONS FOR FIRST TIME USE
# MAKE 3 COMPUTER ACCOUNTS NAMED "Computer_Easy", "Computer_Med" and
# "Computer_Hard" AND SET THEIR STATS HOWEVER YOU LIKE! OTHERWISE UNABLE
# TO PLAY VS AI
class CharacterDoesNotExist(Exception):
pass
class Character(object):
# Change value of default if you want people to start with higher str/def
# when they make their account.
path = 'accounts/.json'
mild_attack = 12, 22
hard_attack = 5, 35
heal_amount = 15, 28
default = 1
def __init__(self, name, health=None, strength=None,
defence=None, gold=0, exp=0, wins=0, losses=0):
self.name = name
self.health = 100 if health is None else health
self.strength = self.default if strength is None else strength
self.defence = self.default if defence is None else defence
self.gold = 0 if gold is None else gold
self.exp = 0 if exp is None else exp
self.wins = 0 if wins is None else wins
self.losses = 0 if losses is None else wins
@classmethod
def load(cls, name):
path = cls.path.format(name)
try:
with open(path) as f:
return cls(**json.load(f))
except Exception:
raise CharacterDoesNotExist(name)
def save(self):
path = self.path.format(self.name)
with open(path, 'w+') as f:
json.dump(self.__dict__, f)
def __str__(self):
return self.name.capitalize()
def upgrade_stat(self): # Upgrade either defence or strength
if self.exp < 100:
print("You don't have enough exp!")
return
target = raw_input("What stat do you want to upgrade?")
if target not in ("strength", "defence"):
print("You cannot do that!")
else:
self.exp -= 100
setattr(self, target, getattr(self, target) + 1)
self.save()
def attack(self, defender, damage_amount):
# You get damage boost, depending on attacker strength level and
# defender defence level.
damage_amount_boost = (self.strength * damage_amount /
(self.strength + defender.defence)) + damage_amount
if damage_amount_boost > defender.health:
overkill = abs(self.health - damage_amount_boost)
defender.health = 0
if overkill > 0:
print(" takes fatal damage from , with overkill!".format(
defender, self, overkill))
else:
print(" takes fatal damage from !".format(defender, self))
else:
defender.health -= damage_amount_boost
print(" takes damage from !".format(
defender, damage_amount_boost, self))
def heal(self, heal_amount):
if (heal_amount + self.health > 100):
self.health = 100
print(" heals back to full health!".format(self))
else:
self.health += heal_amount
print(" heals for !".format(self, heal_amount))
def get_selection(self):
print 'Avalaible moves: 1. Mild Attack, 2. Hard Attack, 3. Heal'
while True:
try:
choice = int(input("Select a move: "))
if not 1 <= choice <= 3:
continue
return choice
except ValueError:
print("The input was invalid. Please try again.")
def make_move(self, player2):
choice = self.get_selection()
if choice == 1:
# mild attack
damage = random.randint(*self.mild_attack)
self.attack(player2, damage)
elif choice == 2:
# mild attack
damage = random.randint(*self.hard_attack)
self.attack(player2, damage)
elif choice == 3:
# mild attack
heal = random.randint(*self.heal_amount)
self.heal(heal)
def win(self):
self.wins += 1
self.exp += 25
self.gold += 100
self.save()
def loose(self):
self.losses += 1
self.save()
class Computer(Character):
def get_selection(self):
print("Computer thinking...")
time.sleep(random.randrange(2, 5))
if self.health <= 35:
# Have the computer heal ~50% of its turns when <= 35
if random.random() >= 0.5:
return 3
return random.randint(1, 2)
elif self.health == 100:
return random.randint(1, 2)
return random.randint(1, 3)
def win(self):
self.wins += 1
def loose(self):
self.losses += 1
def play_round(player1, player2):
player1.health = player2.health = 100
while True:
print("n has health remaining and has health remaining.".format(
player1, player1.health, player2, player2.health))
for _ in range(2):
player1.make_move(player2)
if player2.health <= 0:
print(" looses!".format(player2))
player1.win()
player2.loose()
return
# swap the players
player1, player2 = player2, player1
# Play vs Ai, which you select your difficulty.
def play_vs_ai(human, computer):
while True:
print "Current Score:"
print "You - ".format(human.wins)
print "Computer - ".format(computer.wins)
play_round(computer, human)
print
if raw_input("Play another round?(Y/N)").lower() == "n":
break
def play_1_vs_1(player1, player2): # Play a local 1v1 game.
while True:
print "Current Score:"
print "Player1 - ".format(player1.wins)
print "Player2 - ".format(player2.wins)
play_round(player1, player1)
print
if raw_input("Play another 1v1? (y/n)").lower() != "y":
break
def main_menu(): # Main menu
name = raw_input("Username to load or create?")
try:
player1 = Character.load(name)
except CharacterDoesNotExist:
player1 = Character(name)
player1.save()
while True:
print("What do you want to do?")
move = raw_input(
"1. Battle AI | 2. List stats | 3. Play 1v1 | 4. Upgrade str/def")
if move == "1":
target = raw_input("Easy, med or hard?")
if target.lower() == "easy":
computer = Computer("Computer_Easy")
elif target.lower() == "med": # Set stats however you like
computer = Computer("Computer_Med", strength=2, defence=3)
elif target.lower() == "hard":
computer = Computer("Computer_Hard", strength=5, defence=4)
play_vs_ai(player1, computer)
elif move == "2":
print player1.__dict__
elif move == "3":
target = raw_input("Who do you want to fight?")
try:
player2 = Character.load(target)
except CharacterDoesNotExist:
player2 = Character(target)
player2.save()
play_1_vs_1(player1, player2)
elif move == "4":
while True:
player = raw_input("Which player: or ")
if player == player1.name:
player1.upgrade_stat()
break
# elif player == player2.name:
# player2.upgrade_stat()
# break
else:
print("Exiting..")
return
if __name__ == "__main__":
main_menu()
Some notable changes:
- The
Character
class inherits fromobject
, making it a new-style class. - This uses a
Computer
class that inherits fromCharacter
and overrides some methods to allow the computer to make its decisions and prevent it from saving its wins and losses. - It has a
classmethod
load
that loads a character from a file and asave
method to save it. - The
__str__
magic method makes sure that when we print a character that we see their name - Instead of disallowing some stats (like wins) to be upgraded, explicitly allow only strength and defence to be upgraded (whitelist vs blacklist).
- A
Character
canattack
andheal
. By how much is encoded as a class variable. - Some of the attributes can be reached like for a
dict
, to ease the upgrading of a stat. - There is a
make_move
function that performs a characters move, given a choice of moves. - The
play_round
function is generic enough that it does not care if both players are humans or even both are computers. - The only thing that does not currently work is upgrading the stats of player 2, if they are human.
- You don't need to specify the position in
str.format
strings, if you use them in the order you pass it in. In other words"0 1".format(1, 2)
and" ".format(1, 2)
are equivalent. - It uses a
if __name__ == "__main__":
guard to allow importing from this module. - A new character is automatically created if the loading fails.
- The AI players have default values now. Whether or not they make sense, I don't know (I managed to consistently beat the easy AI...).
- Consistently use
print
as a statement, as it was in Python 2.
Some things which you should still change:
- Make all those comments above your functions into
docstrings
. - Port this to Python 3, as Python 2 will be EOL soon.
Currently your code is a bit all over the place. You have a Character
class that can keep track of how much e.g. exp a character has. But when it comes to adding exp, you instead use a data
dictionary.
Also, instead of saving the Character
to file, you save that dictionary instead. The only thing where you actually use that class is to get the characters name.
So you have two options:
- Get rid of the class and always use the
data
dictionary. - Make all of those things something the
class
handles.
I would choose option 2 here.
Here is a start on that road:
import random
import json
import time
r = random
# INSTRUCTIONS FOR FIRST TIME USE
# MAKE 3 COMPUTER ACCOUNTS NAMED "Computer_Easy", "Computer_Med" and
# "Computer_Hard" AND SET THEIR STATS HOWEVER YOU LIKE! OTHERWISE UNABLE
# TO PLAY VS AI
class CharacterDoesNotExist(Exception):
pass
class Character(object):
# Change value of default if you want people to start with higher str/def
# when they make their account.
path = 'accounts/.json'
mild_attack = 12, 22
hard_attack = 5, 35
heal_amount = 15, 28
default = 1
def __init__(self, name, health=None, strength=None,
defence=None, gold=0, exp=0, wins=0, losses=0):
self.name = name
self.health = 100 if health is None else health
self.strength = self.default if strength is None else strength
self.defence = self.default if defence is None else defence
self.gold = 0 if gold is None else gold
self.exp = 0 if exp is None else exp
self.wins = 0 if wins is None else wins
self.losses = 0 if losses is None else wins
@classmethod
def load(cls, name):
path = cls.path.format(name)
try:
with open(path) as f:
return cls(**json.load(f))
except Exception:
raise CharacterDoesNotExist(name)
def save(self):
path = self.path.format(self.name)
with open(path, 'w+') as f:
json.dump(self.__dict__, f)
def __str__(self):
return self.name.capitalize()
def upgrade_stat(self): # Upgrade either defence or strength
if self.exp < 100:
print("You don't have enough exp!")
return
target = raw_input("What stat do you want to upgrade?")
if target not in ("strength", "defence"):
print("You cannot do that!")
else:
self.exp -= 100
setattr(self, target, getattr(self, target) + 1)
self.save()
def attack(self, defender, damage_amount):
# You get damage boost, depending on attacker strength level and
# defender defence level.
damage_amount_boost = (self.strength * damage_amount /
(self.strength + defender.defence)) + damage_amount
if damage_amount_boost > defender.health:
overkill = abs(self.health - damage_amount_boost)
defender.health = 0
if overkill > 0:
print(" takes fatal damage from , with overkill!".format(
defender, self, overkill))
else:
print(" takes fatal damage from !".format(defender, self))
else:
defender.health -= damage_amount_boost
print(" takes damage from !".format(
defender, damage_amount_boost, self))
def heal(self, heal_amount):
if (heal_amount + self.health > 100):
self.health = 100
print(" heals back to full health!".format(self))
else:
self.health += heal_amount
print(" heals for !".format(self, heal_amount))
def get_selection(self):
print 'Avalaible moves: 1. Mild Attack, 2. Hard Attack, 3. Heal'
while True:
try:
choice = int(input("Select a move: "))
if not 1 <= choice <= 3:
continue
return choice
except ValueError:
print("The input was invalid. Please try again.")
def make_move(self, player2):
choice = self.get_selection()
if choice == 1:
# mild attack
damage = random.randint(*self.mild_attack)
self.attack(player2, damage)
elif choice == 2:
# mild attack
damage = random.randint(*self.hard_attack)
self.attack(player2, damage)
elif choice == 3:
# mild attack
heal = random.randint(*self.heal_amount)
self.heal(heal)
def win(self):
self.wins += 1
self.exp += 25
self.gold += 100
self.save()
def loose(self):
self.losses += 1
self.save()
class Computer(Character):
def get_selection(self):
print("Computer thinking...")
time.sleep(random.randrange(2, 5))
if self.health <= 35:
# Have the computer heal ~50% of its turns when <= 35
if random.random() >= 0.5:
return 3
return random.randint(1, 2)
elif self.health == 100:
return random.randint(1, 2)
return random.randint(1, 3)
def win(self):
self.wins += 1
def loose(self):
self.losses += 1
def play_round(player1, player2):
player1.health = player2.health = 100
while True:
print("n has health remaining and has health remaining.".format(
player1, player1.health, player2, player2.health))
for _ in range(2):
player1.make_move(player2)
if player2.health <= 0:
print(" looses!".format(player2))
player1.win()
player2.loose()
return
# swap the players
player1, player2 = player2, player1
# Play vs Ai, which you select your difficulty.
def play_vs_ai(human, computer):
while True:
print "Current Score:"
print "You - ".format(human.wins)
print "Computer - ".format(computer.wins)
play_round(computer, human)
print
if raw_input("Play another round?(Y/N)").lower() == "n":
break
def play_1_vs_1(player1, player2): # Play a local 1v1 game.
while True:
print "Current Score:"
print "Player1 - ".format(player1.wins)
print "Player2 - ".format(player2.wins)
play_round(player1, player1)
print
if raw_input("Play another 1v1? (y/n)").lower() != "y":
break
def main_menu(): # Main menu
name = raw_input("Username to load or create?")
try:
player1 = Character.load(name)
except CharacterDoesNotExist:
player1 = Character(name)
player1.save()
while True:
print("What do you want to do?")
move = raw_input(
"1. Battle AI | 2. List stats | 3. Play 1v1 | 4. Upgrade str/def")
if move == "1":
target = raw_input("Easy, med or hard?")
if target.lower() == "easy":
computer = Computer("Computer_Easy")
elif target.lower() == "med": # Set stats however you like
computer = Computer("Computer_Med", strength=2, defence=3)
elif target.lower() == "hard":
computer = Computer("Computer_Hard", strength=5, defence=4)
play_vs_ai(player1, computer)
elif move == "2":
print player1.__dict__
elif move == "3":
target = raw_input("Who do you want to fight?")
try:
player2 = Character.load(target)
except CharacterDoesNotExist:
player2 = Character(target)
player2.save()
play_1_vs_1(player1, player2)
elif move == "4":
while True:
player = raw_input("Which player: or ")
if player == player1.name:
player1.upgrade_stat()
break
# elif player == player2.name:
# player2.upgrade_stat()
# break
else:
print("Exiting..")
return
if __name__ == "__main__":
main_menu()
Some notable changes:
- The
Character
class inherits fromobject
, making it a new-style class. - This uses a
Computer
class that inherits fromCharacter
and overrides some methods to allow the computer to make its decisions and prevent it from saving its wins and losses. - It has a
classmethod
load
that loads a character from a file and asave
method to save it. - The
__str__
magic method makes sure that when we print a character that we see their name - Instead of disallowing some stats (like wins) to be upgraded, explicitly allow only strength and defence to be upgraded (whitelist vs blacklist).
- A
Character
canattack
andheal
. By how much is encoded as a class variable. - Some of the attributes can be reached like for a
dict
, to ease the upgrading of a stat. - There is a
make_move
function that performs a characters move, given a choice of moves. - The
play_round
function is generic enough that it does not care if both players are humans or even both are computers. - The only thing that does not currently work is upgrading the stats of player 2, if they are human.
- You don't need to specify the position in
str.format
strings, if you use them in the order you pass it in. In other words"0 1".format(1, 2)
and" ".format(1, 2)
are equivalent. - It uses a
if __name__ == "__main__":
guard to allow importing from this module. - A new character is automatically created if the loading fails.
- The AI players have default values now. Whether or not they make sense, I don't know (I managed to consistently beat the easy AI...).
- Consistently use
print
as a statement, as it was in Python 2.
Some things which you should still change:
- Make all those comments above your functions into
docstrings
. - Port this to Python 3, as Python 2 will be EOL soon.
edited Jul 12 at 9:06
answered Jul 11 at 16:29
Graipher
20.4k42981
20.4k42981
2
Instead of printing a__dict__
prefer usingvars
. Also, if defining__getitem__
and__setitem__
is only useful forself[target] += 1
inupgrade_stat
you shoul favorgetattr
and/orhasattr
instead and handle a failure the same as a restricted attribute. Besides, you may want to check for the XP amount before asking what to upgrade.
â Mathias Ettinger
Jul 12 at 7:12
@MathiasEttinger: Agree, when I was writing it I thought I might need to set and get more attributes. Removed those methods entirely and replaced them with a call tosetattr
andgetattr
. Btw, I am checking that the user has enough exp.
â Graipher
Jul 12 at 8:57
1
But the check comes after asking the question ;)
â Mathias Ettinger
Jul 12 at 9:00
add a comment |Â
2
Instead of printing a__dict__
prefer usingvars
. Also, if defining__getitem__
and__setitem__
is only useful forself[target] += 1
inupgrade_stat
you shoul favorgetattr
and/orhasattr
instead and handle a failure the same as a restricted attribute. Besides, you may want to check for the XP amount before asking what to upgrade.
â Mathias Ettinger
Jul 12 at 7:12
@MathiasEttinger: Agree, when I was writing it I thought I might need to set and get more attributes. Removed those methods entirely and replaced them with a call tosetattr
andgetattr
. Btw, I am checking that the user has enough exp.
â Graipher
Jul 12 at 8:57
1
But the check comes after asking the question ;)
â Mathias Ettinger
Jul 12 at 9:00
2
2
Instead of printing a
__dict__
prefer using vars
. Also, if defining __getitem__
and __setitem__
is only useful for self[target] += 1
in upgrade_stat
you shoul favor getattr
and/or hasattr
instead and handle a failure the same as a restricted attribute. Besides, you may want to check for the XP amount before asking what to upgrade.â Mathias Ettinger
Jul 12 at 7:12
Instead of printing a
__dict__
prefer using vars
. Also, if defining __getitem__
and __setitem__
is only useful for self[target] += 1
in upgrade_stat
you shoul favor getattr
and/or hasattr
instead and handle a failure the same as a restricted attribute. Besides, you may want to check for the XP amount before asking what to upgrade.â Mathias Ettinger
Jul 12 at 7:12
@MathiasEttinger: Agree, when I was writing it I thought I might need to set and get more attributes. Removed those methods entirely and replaced them with a call to
setattr
and getattr
. Btw, I am checking that the user has enough exp.â Graipher
Jul 12 at 8:57
@MathiasEttinger: Agree, when I was writing it I thought I might need to set and get more attributes. Removed those methods entirely and replaced them with a call to
setattr
and getattr
. Btw, I am checking that the user has enough exp.â Graipher
Jul 12 at 8:57
1
1
But the check comes after asking the question ;)
â Mathias Ettinger
Jul 12 at 9:00
But the check comes after asking the question ;)
â Mathias Ettinger
Jul 12 at 9:00
add a comment |Â
up vote
4
down vote
parse_int()
has no reason to be a function, as it doesn't simplify anything.get_selection()
could look like this:def get_selection():
valid_input = False
while (valid_input is False):
print()
choice = input("Select an attack: ")
try:
return int(choice)
except ValueError:
print("The input was invalid. Please try again.")For comparing object identity (i.e.
current_player == player2
orcurrent_player == computer
) useis
. this checks if the objects are the same, rather than if they have the same values. In your case, because you did not override==
they should currently act the same.You can abstract out a single human or computer turn as a function, so that the different combinations of human computer play are all the same code. This will also make it easier to do AI vs. AI duels.
Try to separate the game code from the output code. What if you or someone else wanted to make a different user interface for your game? First try to remove printing from the
Character
class, then try to make a quiet turn function that handles dueling using arguments and return values.Instead of
r = random
you can doimport random as r
. In my opinion however, it is not a good idea to change the name of a module as it makes it harder for readers to figure out which module you are using.
add a comment |Â
up vote
4
down vote
parse_int()
has no reason to be a function, as it doesn't simplify anything.get_selection()
could look like this:def get_selection():
valid_input = False
while (valid_input is False):
print()
choice = input("Select an attack: ")
try:
return int(choice)
except ValueError:
print("The input was invalid. Please try again.")For comparing object identity (i.e.
current_player == player2
orcurrent_player == computer
) useis
. this checks if the objects are the same, rather than if they have the same values. In your case, because you did not override==
they should currently act the same.You can abstract out a single human or computer turn as a function, so that the different combinations of human computer play are all the same code. This will also make it easier to do AI vs. AI duels.
Try to separate the game code from the output code. What if you or someone else wanted to make a different user interface for your game? First try to remove printing from the
Character
class, then try to make a quiet turn function that handles dueling using arguments and return values.Instead of
r = random
you can doimport random as r
. In my opinion however, it is not a good idea to change the name of a module as it makes it harder for readers to figure out which module you are using.
add a comment |Â
up vote
4
down vote
up vote
4
down vote
parse_int()
has no reason to be a function, as it doesn't simplify anything.get_selection()
could look like this:def get_selection():
valid_input = False
while (valid_input is False):
print()
choice = input("Select an attack: ")
try:
return int(choice)
except ValueError:
print("The input was invalid. Please try again.")For comparing object identity (i.e.
current_player == player2
orcurrent_player == computer
) useis
. this checks if the objects are the same, rather than if they have the same values. In your case, because you did not override==
they should currently act the same.You can abstract out a single human or computer turn as a function, so that the different combinations of human computer play are all the same code. This will also make it easier to do AI vs. AI duels.
Try to separate the game code from the output code. What if you or someone else wanted to make a different user interface for your game? First try to remove printing from the
Character
class, then try to make a quiet turn function that handles dueling using arguments and return values.Instead of
r = random
you can doimport random as r
. In my opinion however, it is not a good idea to change the name of a module as it makes it harder for readers to figure out which module you are using.
parse_int()
has no reason to be a function, as it doesn't simplify anything.get_selection()
could look like this:def get_selection():
valid_input = False
while (valid_input is False):
print()
choice = input("Select an attack: ")
try:
return int(choice)
except ValueError:
print("The input was invalid. Please try again.")For comparing object identity (i.e.
current_player == player2
orcurrent_player == computer
) useis
. this checks if the objects are the same, rather than if they have the same values. In your case, because you did not override==
they should currently act the same.You can abstract out a single human or computer turn as a function, so that the different combinations of human computer play are all the same code. This will also make it easier to do AI vs. AI duels.
Try to separate the game code from the output code. What if you or someone else wanted to make a different user interface for your game? First try to remove printing from the
Character
class, then try to make a quiet turn function that handles dueling using arguments and return values.Instead of
r = random
you can doimport random as r
. In my opinion however, it is not a good idea to change the name of a module as it makes it harder for readers to figure out which module you are using.
edited Jul 11 at 16:40
Daniel
4,1032835
4,1032835
answered Jul 11 at 16:14
Peter
59410
59410
add a comment |Â
add a comment |Â
up vote
2
down vote
Try to eliminate repetitions. For example, you have a lot of âÂÂgenerate filename, open, read/writeâ statements. Instead, create a separate class which could use along the lines of
storage = FileStorage(name) # reads "accounts/name.json" into storage.data
storage.data["something"] = whatever
storage.save()
Where else are you repeating yourself a lot? You have
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
But you can simplify this to
active_player, non_active_player = human, computer
...
active_player.calculate_damage(damage, active_player.name, non_active_player.name)
...
# and when the turn ends:
active_player, non_active_player = non_active_player, active_player
And so on.
Repetition is bad because whenever you want to make a change you have to do it in multiple places, and you risk failing to update all the places or updating some of them differently from others.
add a comment |Â
up vote
2
down vote
Try to eliminate repetitions. For example, you have a lot of âÂÂgenerate filename, open, read/writeâ statements. Instead, create a separate class which could use along the lines of
storage = FileStorage(name) # reads "accounts/name.json" into storage.data
storage.data["something"] = whatever
storage.save()
Where else are you repeating yourself a lot? You have
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
But you can simplify this to
active_player, non_active_player = human, computer
...
active_player.calculate_damage(damage, active_player.name, non_active_player.name)
...
# and when the turn ends:
active_player, non_active_player = non_active_player, active_player
And so on.
Repetition is bad because whenever you want to make a change you have to do it in multiple places, and you risk failing to update all the places or updating some of them differently from others.
add a comment |Â
up vote
2
down vote
up vote
2
down vote
Try to eliminate repetitions. For example, you have a lot of âÂÂgenerate filename, open, read/writeâ statements. Instead, create a separate class which could use along the lines of
storage = FileStorage(name) # reads "accounts/name.json" into storage.data
storage.data["something"] = whatever
storage.save()
Where else are you repeating yourself a lot? You have
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
But you can simplify this to
active_player, non_active_player = human, computer
...
active_player.calculate_damage(damage, active_player.name, non_active_player.name)
...
# and when the turn ends:
active_player, non_active_player = non_active_player, active_player
And so on.
Repetition is bad because whenever you want to make a change you have to do it in multiple places, and you risk failing to update all the places or updating some of them differently from others.
Try to eliminate repetitions. For example, you have a lot of âÂÂgenerate filename, open, read/writeâ statements. Instead, create a separate class which could use along the lines of
storage = FileStorage(name) # reads "accounts/name.json" into storage.data
storage.data["something"] = whatever
storage.save()
Where else are you repeating yourself a lot? You have
if (current_player == human):
computer.calculate_damage(damage, human.name, computer.name)
else:
human.calculate_damage(damage, computer.name, human.name)
But you can simplify this to
active_player, non_active_player = human, computer
...
active_player.calculate_damage(damage, active_player.name, non_active_player.name)
...
# and when the turn ends:
active_player, non_active_player = non_active_player, active_player
And so on.
Repetition is bad because whenever you want to make a change you have to do it in multiple places, and you risk failing to update all the places or updating some of them differently from others.
answered Jul 11 at 19:04
Roman Odaisky
2712
2712
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f198293%2ftext-turn-based-dueling-game%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password