I'm new to OOP and am practicing putting together a little bit more complex programs using various classes and implementing principles like Inheritance. I've created a Ping-Pong simulation that has a Player class which contains the probability that the player will win their serve. Then I have a PingPong class which is a subclass of the super class RacquetSports. Each instance is a single game, with the ability to change server, and record who won, and whether or not it was a shut-out. Finally, I have a SimStats class whose purpose is to record the stats across "n" number of games.
My problem is that it seems like my play_game function is not correctly firing, when I place a print statement in there it never triggers. My current result when running the whole program is that Player1 has 10 wins no shutouts, and Player2 has zero of both.
Finally, any suggestions on better OO practice would also be appreciated. Here is my player class:
from random import random
class Player(object):
def __init__(self, prob_win):
self.prob = prob_win
self.points = 0
def wins_serve(self):
return self.prob >= random()
def add_point(self):
self.points += 1
def get_score(self):
return self.points
My RacquetSports class:
from abc import ABCMeta, abstractmethod
from player import Player
class RacquetSport(object):
__metaclass__ = ABCMeta
def __init__(self, prob1, prob2):
self.player1 = Player(prob1)
self.player2 = Player(prob2)
self.server = self.player1
def play_game(self):
while not self.game_over():
self.sim_point()
#abstractmethod
def type(self):
pass
def chg_server(self):
if self.server == self.player1:
self.server = self.player2
else:
self.server = self.player1
def sim_point(self):
if self.server.wins_serve():
self.server.add_point()
else:
self.chg_server()
#abstractmethod
def game_over(self):
pass
def get_scores(self):
return self.player1.get_score(), \
self.player2.get_score()
def return_stats(self):
p1_score, p2_score = self.get_scores()
print(p1_score, p2_score)
won = 'p1'
if p2_score > p1_score:
won = 'p2'
return won, self.__shutout(p1_score, p2_score)
#staticmethod
#abstractmethod
def __shutout(score1, score2):
pass
My PingPong and SimStats classes, as well as my calling code:
from racquet import RacquetSport
class PingPong(RacquetSport):
def type(self):
return 'Ping Pong'
def game_over(self):
return self.player1.get_score == 11 or \
self.player2.get_score == 11
#staticmethod
def __shutout(score1, score2):
return abs(score1 - score2) == 11
class SimStats(object):
def __init__(self):
# First field is games won, second is shutouts.
self.gms_won_p1 = [0] * 2
self.gms_won_p2 = [0] * 2
def update(self, game):
won, shutout = game.return_stats()
if won == 'p1':
self.gms_won_p1[0] += 1
if shutout:
self.gms_won_p1[1] += 1
else:
self.gms_won_p2[0] += 1
if shutout:
self.gms_won_p2[1] += 1
def print_results(self):
tot_games = self.gms_won_p1 + self.gms_won_p2
print('Wins for Player 1 = {} Shutouts = {}\n'
'Wins for Player 2 = {} Shutouts = {}'.format(*tot_games))
if __name__ == '__main__':
stats = SimStats()
for x in range(1, 11):
game = PingPong(.5, .5)
stats.update(game)
stats.print_results()
Your first problem is that you never call play_game. My guess is that you intend it to work like this:
if __name__ == '__main__':
stats = SimStats()
for x in range(1, 11):
game = PingPong(.5, .5)
game.play_game()
stats.update(game)
stats.print_results()
Next, you have a bug that will cause the entire game to last forever. Take a look at these lines:
def game_over(self):
return self.player1.get_score == 11 or \
self.player2.get_score == 11
get_score is a function, so you need to call it:
def game_over(self):
return self.player1.get_score() == 11 or \
self.player2.get_score() == 11
Related
I need help to be more clear how to make my code correctly. Now it's work but I still think the code logic not correct :)
I have one class with several attributes:
class Game:
def __init__(self, player1, player2, player3, player4, score_pair1, score_pair2):
self.player1 = player1
self.player2 = player2
self.player3 = player3
self.player4 = player4
self.score_pair1 = score_pair1
self.score_pair2 = score_pair2
self.Online_Players_Dict = []
self.Temp_Players_Dict = []
self.Temp_import_players = []
I have method inside of this class for work with all attributes:
def create_temp_players(self):
for i in ('player1', 'player2', 'player3', 'player4'):
temp = self.__getattribute__(i)
if not isinstance(temp, Player):
for ii in self.Temp_import_players:
if ii['name'] == self.__getattribute__(i):
self.__setattr__(i, Player(self.__getattribute__(i), ii['score'], ii['daily_score']))
b = self.__getattribute__(i)
a = dict(name=b.name, score=b.score, daily_score=b.daily_score)
self.Online_Players_Dict.append(a)
temp = self.__getattribute__(i)
if not isinstance(temp, Player):
self.__setattr__(i, Player(self.__getattribute__(i)))
b = self.__getattribute__(i)
a = dict(name=b.name, score=b.score, daily_score=b.daily_score)
self.Online_Players_Dict.append(a)
self.Temp_Players_Dict.append(a)
And everything work correct, but I think not right to use (getattribute) and (setattr) here. But I can't find other way to correct interaction attributes in the code.
Do you have any idea how to make it better?
PS: Class Player also has attribute:
class Player:
def __init__(self, name, score=500, daily_score=0):
self.name = name
self.score = score
self.daily_score = daily_score
I changed code like this
for player, i in zip(self.players, (0, 1, 2, 3)):
if not isinstance(self.players[i], Player):
for ii in self.Temp_import_players:
if ii['name'] == self.players[i]:
self.players[i] = Player(self.players[i], ii['score'], ii['daily_score'])
a = dict(name=self.players[i].name, score=self.players[i].score, daily_score=self.players[i].daily_score)
self.Online_Players_Dict.append(a)
if not isinstance(self.players[i], Player):
self.players[i] = Player(self.players[i])
a = dict(name=self.players[i].name, score=self.players[i].score, daily_score=self.players[i].daily_score)
self.Online_Players_Dict.append(a)
self.Temp_Players_Dict.append(a)
Later will refactoring all self.playerX to self.player[X]
One thing that I am struggling with while trying to learn concepts of OOP is creation of class instances. Most of the tutorials online will explain basic principles like Init, Self, Inheritance etc.. but when it comes to creating instances of the class itself it is usually reduced to something like that:
emp1 = Employee("John")
emp2 = Employee("Leviticus")
In reality most of us beginners will want to create instance of a class dynamically (On press of button etc..) not directly in code and also will be interessted in keeping track of our instances. What I was able to come up is this:
from tkinter import *
import random
class Point:
_registry = []
def __init__(self, x_pos, y_pos):
self._registry.append(self)
self.x_pos = x_pos
self.y_pos = y_pos
print(self.x_pos, self.y_pos)
def create_point():
Point(random.randint(1,20),random.randint(1,20))
window = Tk()
button = Button(window, text = "Add point", command=create_point)
button.pack()
window.mainloop()
Can someone advise if this is a proper way to do this? Shouldnt the fnction create_point be within Point class? What is the proper way to keep track of instances and later delete them? Shall I use some sort of ID attribute to keep track and "itemize" my instances? Is there any good source with tutorial that deals with that?
Thank you
Jacob
after completing tutorial at: https://pythonschool.net/category/oop.html I managed to get what I wanted by doing:
class Point:
def __init__(self,ID, xcor, ycor):
self._ID = ID
self._xcor = xcor
self._ycor = ycor
def report(self):
return {"ID:":self._ID,"xcor":self._xcor,"ycor":self._ycor}
def get_ID(self):
return self._ID
class Points:
def __init__(self):
self._points = []
def add_point(self, point):
self._points.append(point)
def return_index_from_ID(self, ID):
for i, o in enumerate(self._points):
if o.get_ID() == ID:
break
return i
def delete_point(self, index):
del self._points[index]
def print_contents(self):
for x in self._points:
print(x.report())
def return_empty_ID(self):
list = []
for x in self._points:
list.append(x.get_ID())
if not list:
return 1
else:
for i in range(1, max(list)+2):
if i not in list: break
return i
def add_point( xcor, ycor, points):
points.add_point(Point(points.return_empty_ID(), xcor, ycor))
def delete_point(ID, points):
points.delete_point(ID)
Simple main function for testing to show what I was after:
from point_class import *
myPoints = Points()
noexit = True
while noexit:
print("**********************************************")
print("0 - Exit")
print("1 - Add Point")
print("2 - Print Points")
print("3 - Delete Points")
print("**********************************************")
choice = int(input("Option selected: "))
if choice == 0:
noexit = False
elif choice == 1:
add_point(3,5,myPoints)
elif choice == 2:
myPoints.print_contents()
elif choice == 3:
ID = int(input("Please insert ID of point: "))
delete_point(myPoints.return_index_from_ID(ID),myPoints)
I am trying to make a pokemon game. In this game, I am trying to have pokemon (specific ) classes, which extend a pokemon class(general)
for example, Charmander class extends the pokemon class
However, whenever I extend the pokemon, I get a name error, I tried importing pokemon before extending it, and I still got an error
This is my pokemon.py:
from Charmander import *
from Bulbasaur import *
from Turtwig import *
class Pokemon:
def __init__(self, current_hp, attack, defense):
self.name = "PlaceHolder"
self.pokemon_type = "PlaceHolder"
self.current_hp = current_hp
self.attack = attack
self.defense = defense
self.fainted = False
def getName(self):
return self.name
def getType(self):
return self.pokemon_type
def getCurrentHP(self):
return self.current_hp
def getHealth(self):
return self.current_hp
def getAttack(self):
return self.attack
def getDefense(self):
return self.defense
def getFainted(self):
return self.fainted
def printStatus(self):
print(self.name)
print(str(self.current_hp))
def takedamage(self, amount):
self.current_hp -= amount
def tackle(self, opponent):
opponent.current_hp -= self.attack
def die(self, opponent):
self.fainted = True
if self.current_hp == 0:
print("You Lose!")
elif(opponent.current_hp == 0):
print("You win!")
def checkDead(self, opponent):
if self.current_hp == 0 or opponent.current_hp == 0:
self.die(opponent)
def assignPokemon(self, player):
if player == "Charmander":
player = Charmander(200, 20, 20)
if player == "Bulbasaur":
player = Bulbasaur(200, 20, 20)
if player == "Turtwig":
player = Turtwig(200, 20, 20)
return player
This is my Charmander.py:
from pokemon import *
class Charmander(Pokemon):
pass
def __init__(self, current_hp, attack, defense):
self.name = "Charmander"
self.type = "Fire"
self.current_hp = 200
self.attack = 10
self.defense = 10
def ember(self, opponent):
opponent.takeDamage(40)
def will_o_wisp(self, opponent):
return
def flamethrower(self, opponent):
opponent.takeDamage(90)
This is the error I am receiving:
/Users/alex/PycharmProjects/test/venv/bin/python "/Users/alex/Documents/SCSNoonan Computer Science/Slow Pokemon/main.py"
Traceback (most recent call last):
File "/Users/alex/Documents/SCSNoonan Computer Science/Slow Pokemon/main.py", line 1, in
from pokemon import *
File "/Users/alex/Documents/SCSNoonan Computer Science/Slow Pokemon/pokemon.py", line 1, in
from Charmander import *
File "/Users/alex/Documents/SCSNoonan Computer Science/Slow Pokemon/Charmander.py", line 2, in
class Charmander(Pokemon):
NameError: name 'Pokemon' is not defined
This error is probably due to circular imports. Also, the pass statement before your __init__ method inside your Charmander class is useless.
You don't need to do this in your pokemon.py file
from Charmander import *
from Bulbasaur import *
from Turtwig import *
The method assignPokemon doesn't need self parameter so it should either be a staticmethod or be outside of your Pokemon class.
You should put all pokemon classes in a file together and then you could call assignPokemon in another file, for example:
# main.py
from pokemon import Charmander, Bulbasaur, Turtwig
def assignPokemon(player):
if player == "Charmander":
player = Charmander(200, 20, 20)
if player == "Bulbasaur":
player = Bulbasaur(200, 20, 20)
if player == "Turtwig":
player = Turtwig(200, 20, 20)
return player
I have an assignment to create a code that would define 2 classes, a player and a team each of these having some parameters. Player is supposed to have a name, a number of skis, a number of sledges and a player index(number of games played by the player before).
I managed to define these attributes of the class but I'm having a hard time implementing the team class. Team is supposed to hold the name of the team and the number of players-the players cannot be just their names it must link to the class instance(player). I don't understand how to use the information provided in the player instance to implement team. Here's my code so far:
class Player:
def __init__(self, name, skis, index):
self.name = name
self.sledges = []
self.skis = []
self.index = index
pass
class Team:
def __init__(self, name, players):
self.name = name
self.players = [Player]
pass
def get_players_count()
def get_transport_capacity()
def get_average_index()
*Update
Thank you for your help, I have one more function to add, a function that would return the number of passengers a team could accommodate. I've tried something like this but I don't think the syntax is correct. The user inputs the number of places in each sledge so I need to iterate over the values in the list to get the number of places.
def get_transport_capacity(self):
skis = len(Player.skis)
for i in Player.sledges:
sledges += Player.sledges[i]
capacity = skis + sledges
return capacity
class Player:
def __init__(self, name, index):
self.name = name
self.sledges = []
self.skis = []
self.index = index
class Team:
def __init__(self, name, players):
self.name = name
self.players = players
def get_players_count(self):
return len(self.players)
def add_player(self, player):
self.players.append(player)
def get_average_index(self):
indexes = sum(list(map(lambda p: p.index, self.players)))
count = self.get_players_count()
return float(indexes) / count
Usage:
a = Player('AName', 2)
b = Player('BName', 11)
team = Team('TeamName', [a, b])
instead of
self.players = [Player]
why not:
self.players = []
And then have a method to add players to the team as in:
def add_player(self, player):
# ensure optionally that player is a PLayer instance
assert type(player) == Player
self.players += player
I'm trying to simulate a game of Counter Strike. Basically I have two teams with different players (all the players are identical for now) and I want them to "fight" and when all the players on one team are dead, the simulation should end.
I'm trying to understand why the simulation I'm running never ends. I feel like I'm misunderstanding some core element of simpy but I don't really know what.
All of the process and simpy related code are in main.py and player.py.
I'm trying to get my simulation to end once every player has "died".
Basically I want every player to be a process that constantly checks their surrounding area (the node they are in which is represented by the Hotspot class) to see if there are any enemies. If there are any enemies they will choose one at random and "attack" them. Once all of the players from any team have health below 0 the simulation should end and the team that won should increment their win count by 1.
EDIT: Also of note, when I ran it through pdb it seemed like none of the player's health were decreasing and that the play method wasn't being run.
EDIT 2: I don't think all of the code needs to be read to find the problem, I think it's mostly in the main and player files but I'm not 100% sure because the code loops infinitely without error
Here is my code
main.py
from player import Player
from game_map import Game_Map
from team import Team
from sides import Sides
import simpy
import pdb
def main():
team_a = Team("Team_A", Sides.CT)
team_b = Team("Team_B", Sides.T)
gmap = Game_Map()
gmap.spawn_team(team_a)
gmap.spawn_team(team_b)
env = simpy.Environment()
for team in (team_a, team_b):
for player in team.players:
env.process(player.play(env))
env.run(until=round(team_a, team_b, env))
def round(team_a, team_b, env):
while True:
if team_a.all_dead():
team_b.round_wins += 1
print team_b
env.exit()
if team_b.all_dead():
team_a.round_wins += 1
print team_a
env.exit()
if __name__ == "__main__":
main()
player.py
import simpy
from sides import Sides
import numpy as np
import pdb
class Player(object):
""" Class that represents a CSGO player"""
def __init__(self, steam_id, team, acc, hs_percentage):
# the player's id
self.steam_id = steam_id
# percentage of shots that hit, accuracy
self.acc = acc
# percentage of hits that hit the head
self.hs_percentage = hs_percentage
# the team
self.team = team
# the player's health, this changes when the teams "fight"
self.health = 100
# the current hotspot that the player is in
self.current_location = 0
# if the player is alive or dead
self.is_alive = True
def play(self, env):
"""Process that simulates the player's actions. This is run once every round until
the round is over"""
while(self.is_alive):
target = self.choose_target()
if target == -1:
continue
yield env.timeout(5)
else:
target.inflict_self(self.determine_damage())
yield env.timeout(5)
def determine_damage(self):
"""The amount of damage the player will inflict on the enemy"""
return 27
def choose_target(self):
"""Choose a target to attack from the enemies in the hotspot"""
# 1 - side converts 0 to 1 and 1 to 0
enemy_list = self.current_location.players[1 - self.team.side]
num_enemies = len(enemy_list)
# if there are no enemies currently in the same location of the player
# simply return 0
if num_enemies == 0:
return -1
# pick an enemy randomly from the list of enemies and return their object
return enemy_list[np.random.random_integers(0, num_enemies - 1)]
def get_side(self):
return self.team.side
def inflict_self(self, damage):
"""Inflict damage onto own class. If damage moves health below 0, mark the
player as "Dead" and remove them from the map"""
self.health = self.health - damage
if self.health <= 0:
self.current_location.players[self.team.side].remove(self)
self.is_alive = False
def __str__(self):
return "Steam id: {0}\tIs Alive: {1}\tCurrent Location: {2}".format(self.steam_id, self.is_alive, self.current_location)
def tests():
return
if __name__ == "__main__":
tests()
game_map.py
import networkx as nx
from hotspot import Hotspot
import matplotlib.pyplot as plt
class Game_Map(object):
""" Generic map that represents general outline of all counter strike maps"""
def __init__(self):
self.graph = nx.Graph()
self.spawns = [Hotspot()]
self.graph.add_node(self.spawns[0])
def add_team(team):
#side = team.side
# side is 0 because for testing the simulation
# we are only using one node
side = 0
for player in team.players:
self.spawns[side].move_into(player)
def spawn_team(self, team):
for player in team.players:
self.spawns[0].move_into(player)
def draw(self):
nx.draw(self.graph)
plt.show()
def tests():
"""Tests to see that Game_Map class works properly"""
# initialize the map
gmap = Game_Map()
gmap.draw()
# if this module is being run explicitly from the command line
# run tests to assure that this module is working properly
if __name__ == "__main__":
tests()
hotspot.py
import simpy
from player import Player
from team import Team
from sides import Sides
class Hotspot(object):
"""Hotspots are the representation for different areas of the map. This is where players 'fight'."""
def __init__(self):
self.players = [[], []]
def move_into(self, player):
side = player.get_side()
self.players[side].append(player)
player.current_location = self
return 1
def __eq__(self, other):
return id(self) == id(other)
def __hash__(self):
return id(self)
def tests():
"""Tests to see that hotspot works properly"""
hotspot_list = []
for i in range(5):
hotspot_list.append(Hotspot())
for spot in hotspot_list:
team_a = Team("team_a", Sides.CT)
team_b = Team("team_b", Sides.T)
spot.move_into(Player(1, team_a, .5, .5))
spot.move_into(Player(1, team_b, .5, .5))
print "Hotspot id = {0}".format(id(spot))
for team in spot.players:
for player in team:
print "player = {0} in team {1}".format(player, player.team)
if __name__ == "__main__":
tests()
sides.py
class Sides(object):
"""Enum object, simply represents CT (Counter Terrorists) as 0 and
T (Terrorists) as 1"""
CT, T = range(2)
team.py
from player import Player
class Team(object):
"""Class that holds critical team information"""
def __init__(self, name, side):
self.round_wins = 0
self.players = []
self.name = name
self.side = side
self.generate_team()
def all_dead(self):
count = 0
for player in self.players:
if player.is_alive == False:
count += 1
if count == 5:
return True
else:
return False
def __str__(self):
rep = "Team: {0}, Round Wins: {1}\n".format(self.name, self.round_wins)
for player in self.players:
rep += player.__str__() + '\n'
return rep
def generate_team(self):
for i in range(5):
self.players.append(Player(1, self, .5, .2))
__rep__ = __str__
requirements.txt
decorator==3.4.2
matplotlib==1.4.3
mock==1.0.1
networkx==1.9.1
nose==1.3.6
numpy==1.9.2
pyparsing==2.0.3
python-dateutil==2.4.2
pytz==2015.2
scipy==0.15.1
simpy==3.0.7
six==1.9.0
Your round() function is the culprit:
env.run(until=round(team_a, team_b, env))
def round(team_a, team_b, env):
while True:
if team_a.all_dead():
team_b.round_wins += 1
print team_b
env.exit()
if team_b.all_dead():
team_a.round_wins += 1
print team_a
env.exit()
The function contains an infinite loop without any yields. This means it never returns and env.run() isn’t even executed.
I have a feeling this might be an infinite loop:
while(self.is_alive):
target = self.choose_target()
if target == -1:
continue
yield env.timeout(5)
You probably want to yield before the continue (which is unneeded anyway).