Reputation: 415
I'm new to OOP. I'd like to simulate a soccer match. How do I access Play instance variables in the Player/Offender/Defender classes? If another structure is better, please help.
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
def move_to(self, x, y):
self.x = (self.x + x) / 2
self.y = (self.y + y) / 2
## Loop through Play.Players to find nearest. How do I access Play.Players?
def nearest(self):
return nearest
class Offender(Player):
def __init__(self, x, y):
super().__init__(x, y)
# Move to ball.
def strategy():
ball_x = Play.ball_x # how do I access Play.ball_x
ball_y = Play.ball_y # how do I access Play.ball_y
self.move_to(ball_x, ball_y)
class Defender(Player):
def __init__(self, x, y):
super().__init__(x, y)
# Move to nearest player
def strategy(self):
nearest = self.nearest()
self.move_to(nearest)
class Play:
def __init__(self, offense_players, defense_players, ball_x, ball_y):
self.offense = [Offender(player) for player in offense_players]
self.defense = [Defender(player) for player in defense_players]
self.players = self.offense + self.defense
self.ball_x = ball_x
self.ball_y = ball_y
def simulate(self):
while True:
for player in self.players:
player.strategy()
if __name__ == "__main__":
Play().simulate()
Instead of having Offender
and Defender
classes, I have one for each position, i.e. Striker(Player)
, Midfielder(Player)
, Goalie(Player)
, etc. which is why I'd like to store their respective strategy
in their class and not in the Play
class.
Upvotes: 4
Views: 1414
Reputation: 6144
I just elaborate the idea from the comment of martineau: We just pass the Play
instance as argument to the relevant methods of Player
. I also wrote out a very first draft for the nearest()
method, but you might want to improve its logic. I was just drafting this to demonstrate how you could solve your OOP design problem.
import typing
class Player:
def __init__(self, x: float, y: float):
self.x = x
self.y = y
def move_to(self, x: float, y: float) -> None:
self.x = (self.x + x) / 2
self.y = (self.y + y) / 2
def nearest(self, play: "Play") -> "Player":
# This must yet be adapted to handle the edge case of two players
# having equal distance to the current player. You didn't specify the
# desired output for that case, hence I just ignored that scenario for now.
return min([
p for p in play.players
if p.x != self.x or p.y != self.y, # this is to
# exclude the player itself.
# This is a buggy logic, because it relies on an assumption that is not
# validated in the code (the assumption that two different players never
# have identical coordinates). You might want to introduce a uniqe `id`
# instance variable to the Player class, to handle this identification in a
# clean way.
],
key=lambda p: (self.x - p.x)**2 + (self.y - p.y)**2
)
class Offender(Player):
def __init__(self, x: float, y: float):
super().__init__(x, y)
# Move to ball.
def strategy(self, play: "Play") -> None:
self.move_to(play.ball_x, play.ball_y)
class Defender(Player):
def __init__(self, x: float, y: float):
super().__init__(x, y)
# Move to nearest player
def strategy(self, play: "Play") -> None:
self.move_to(self.nearest(play=play)
class Play:
def __init__(
self,
offense_players: typing.List["Offender"],
defense_players: typing.List["Defender"],
ball_x: float,
ball_y: float,
):
self.offense = offense_players
self.defense = defense_players
self.players = self.offense + self.defense
self.ball_x = ball_x
self.ball_y = ball_y
def simulate(self) -> None:
while True: # this is still a bad condition, you might want to change this. However you didn't specify your desired logic, so I didn't change it.
for player in self.players:
player.strategy(self, play=self)
Upvotes: 1
Reputation: 31
Not sure how much this will be helpful for you, as the implementation is in C++
You can checkout my implementation for a similar problem statement https://github.com/rimpo/footballcpp
For understanding the arcade game framework implementation for which the above bot was written, checkout http://richard-shepherd.github.io/coding-world-cup/index.html
Upvotes: 2
Reputation: 7210
I would do the following:
keep track of game state in another (data) class:
class GameState:
def __init__(self, offense_players, defense_players, ball_x, ball_y):
self.offense = offense_players
self.defense = defense_players
self.players = self.offense + self.defense
self.ball_x = ball_x
self.ball_y = ball_y
You may even wish to use python3.7 dataclasses
(and some other features) for this (although it is not at all necessary)
from dataclasses import dataclass
from typing import List
@dataclass
class GameState:
offense: List[Offender]
defense: List[Defender]
ball_x: float
ball_y: float
@property
def players(self):
return offense + defense
Players then take this state in their strategy and are expected to update their internal state (like position). nearest
player is implemented by taking the minimum l2 distance between other players using the key argument to min
that takes a function of another player, p
which is written using lambda
.
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
def move_to(self, x, y, other_players):
self.x = (self.x + x) / 2
self.y = (self.y + y) / 2
def nearest(self):
return nearest
class Offender(Player):
def __init__(self, x, y):
super().__init__(x, y)
# Move to ball.
def strategy(self, game_state):
ball_x = game_sate.ball_x # how do I access Play.ball_x
ball_y = game_state.ball_y # how do I access Play.ball_y
self.move_to(ball_x, ball_y)
class Defender(Player):
def __init__(self, x, y):
super().__init__(x, y)
# Move to nearest player
def strategy(self, game_state):
# we assume you are moving to offensive players - which
# you will not be apart of
nearest = min(
game_state.offense
key=lambda p: (
(self.x - p.x) **2 + (self.y - p.y) ** 2
) ** (1/2) # take the l2 norm to find the "closest" player to you
)
self.move_to(nearest.x, nearest.y)
Then, play the game
class Play:
def __init__(self, game_state):
self.game_state = game_state
def simulate(self):
while True:
for player in self.game_state.players:
player.strategy(self.game_state)
if __name__ == "__main__":
Play(GameState(
[Offender(-1, 0), Offender(-1, -1), ...]
[Defender(1, 0), Offender(1, -1), ...]
)).simulate()
You could then implement some actual players
class TiernaDavidson(Defender):
def strategy(self, *args, **kwargs):
return better_strategy(*args, **kwargs)
You will have to ask her for the implementation of better_strategy
;)
Upvotes: 1