Python OOP - Soccer Simulation - python

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.

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

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)

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 ;)

Related

Need help on snake program about user's input

So, I'm writing a snake program using the tkinter Library. The program is globally working but I have a little problem with the inputs' treatment indeed if i give two input too quickly only the last one will be interpret. And i don't really know how to solve this i try to force the update after every player's input but it's clearly not the good solution because it force the snake to move and make it able to teleport so I'm would be glad if someone has an idea to solve this issue. There is my code I'm sure that it could be improved but for now I would like to focus on the first issue.
import tkinter as tk
import numpy.random as rd
class snake:
def __init__(self,n,m):
self.n = n
self.m = m
self.body = [(n//2,m//2),(n//2,m//2-1)]
self.lenght = 2
self.food = (0,0)
self.relocate_food()
self.Game = -2
self.vector = (0,1) #(0,-1) = up, (0,1) = right, (0,1) = down, (-1,0) = left
self.speed = 120
def up(self):
self.vector = (-1,0)
def right(self):
self.vector = (0,1)
def down(self):
self.vector = (1,0)
def left(self):
self.vector = (0,-1)
def relocate_food(self):
x = rd.randint(0,self.n)
y = rd.randint(0,self.m)
i = 0
test = True
while i<self.lenght and test:
if (x,y) == self.body[i]:
test = False
self.relocate_food()
else:
i += 1
if i == self.lenght:
self.food = (x,y)
def collide(self):
head = self.body[0]
for i in range(1,self.lenght):
if head == self.body[i]:
self.Game = -1
break
x,y = head
if x>=self.n or y>=self.m or x<0 or y<0:
self.Game = -1
def eat(self):
head = self.body[0]
if head == self.food:
self.lenght +=1
x0, y0 = self.body[-1]
x1, y1 = self.body[-2]
x = x0 - x1
y = y0 - y1
self.body.append((x0+x,y0+y))
self.relocate_food()
if self.lenght%5 == 0:
self.speed = int(self.speed * 0.90)
def move(self):
dx, dy = self.vector
last_x, last_y = self.body[0]
new_x = last_x + dx
new_y = last_y + dy
self.body[0] = (new_x, new_y)
for k in range(1, self.lenght):
x, y = self.body[k]
self.body[k] = (last_x,last_y)
last_x, last_y = x, y
return
class screen(snake):
def __init__(self,root,n,m):
snake.__init__(self,n,m)
root.minsize(n*20,m*20)
root.maxsize(n*20,m*20)
root.configure(background='white')
self.root = root
self.n = n
self.m = m
self.speed = 130
self.canvas = tk.Canvas(root, width = n*20, height =m*20,bg='black')
self.canvas.bind_all("<Key-Up>",self.move_up)
self.canvas.bind_all("<Key-Down>",self.move_down)
self.canvas.bind_all("<Key-Left>",self.move_left)
self.canvas.bind_all("<Key-Right>",self.move_right)
self.canvas.grid(row=1,column=0)
self.draw_snake()
self.draw_food()
def draw_snake(self):
y,x = self.body[0]
self.canvas.create_rectangle(x*20,y*20,(x+1)*20,(y+1)*20,fill= 'red4')
for k in range(1,self.lenght):
y,x = self.body[k]
self.canvas.create_rectangle(x*20,y*20,(x+1)*20,(y+1)*20,fill= 'red')
def draw_food(self):
y,x =self.food
self.canvas.create_rectangle(x*20,y*20,(x+1)*20,(y+1)*20,fill= 'green')
def move_up(self,event):
if self.Game == -2:
self.Game =0
self.up()
self.update()
else:
self.up()
def move_down(self,event):
if self.Game == -2:
self.Game =0
self.down()
self.update()
else:
self.down()
def move_left(self,event):
if self.Game == -2:
self.Game =0
self.left()
self.update()
else:
self.left()
def move_right(self,event):
if self.Game == -2:
self.Game =0
self.right()
self.update()
else:
self.right()
def update(self):
if self.Game == -2:
return
self.move()
self.eat()
self.collide()
if self.Game == -1:
self.root.destroy()
return
self.canvas.delete("all")
self.draw_snake()
self.draw_food()
self.root.after(self.speed,self.update)
window = tk.Tk()
snake = screen(window,35,35)
snake.update()
window.mainloop()
This is not really a bug. Your animation uses an 'update' function that is executed every 120ms. So if you hit 2 arrow keys within 120ms (i.e. between two successive calls of 'update'), only the last hit is considered, because only one translation vector can be considered for each snake update. Nobody can blame you on that point, as time controlled animation is a discrete process with a given time window. It's the only solution to get fluid and regular animation (all video games are based on such a process), so that's clearly correct.
However, your code may still be improved on several aspects. For instance, at each animation frame, you delete all Canvas items and create a whole new set of items ('create_rectangle') for the snake elements and the food. This is not very efficient. It would be better to simply change the coordinates of the items (check the Canvas.coords function from the doc). Note that animating a snake simply requires to move the previous tail position to the new head position, to give the illusion of a moving beast. So moving only 1 item (2 items when eating food) is necessary at each frame, which is must faster to process.
Thank Furas for the basic idea it was what i needed. New code with my correction :
def __init__(self, root,n,m):
"""
"""
self.input = []
"""
"""
def move_up(self,event):
if self.Game == -2:
self.Game =0
self.up()
self.update()
else:
self.input.append(0)
"""
Same for all the move
"""
def update(self):
if self.Game == -2:
return
if len(self.input)>3: #Make sure that the player doesn't stack instruction
self.pop()
try:
input = self.input.pop(0)
except:
input = -1
if input == 0:
self.up()
elif input == 1:
self.right()
elif input == 2:
self.down()
elif input == 3:
self.left()
self.move()
self.eat()
self.collide()
if self.Game == -1:
self.root.destroy()
return
self.canvas.delete("all")
self.draw_snake()
self.draw_food()
self.root.after(self.speed,self.update)

Classes: Interacting with 2 class instances in one function

It's my first time with classes in python and I quickly wrote a simple class which lets you move rocket by coordinates. I don't know though how to make a function called let's say "distance" that would return distance between two different instances(rockets). Just to be clear, I know how to calculate the distance, I don't know how to build a function
class Rocket:
def __init__(self , start = (0, 0)):
self.start = start
self.x = self.start[0]
self.y = self.start[1]
if not self.crash():
self.start = (0,0)
print("You crashed!! Your position has been restarted to (0,0)")
def __repr__(self):
return "tbd"
def get_position(self):
return "Your curret posiiton is {}".format(self.start)
def move_side(self,x):
self.x += x
self.start = (self.x, self.y)
def move_up(self,y):
self.y += y
self.start = (self.x, self.y)
if not self.crash():
self.start = (0,0)
print("You crashed!! Your position has been restarted to (0,0)")
def move(self,x,y):
self.x += x
self.y += y
self.start = (self.x,self.y)
def land_rocket(self):
self.y = 0
self.start = (self.x,self.y)
def crash(self):
if self.y >= 0:
return True
return False
def distance(self,other):
return "???"
You need to define a class method that takes an extra argument, which is the object from which you want to calculate the distance.
To apply the cartesian distance formula, know that ** stands for exponentiation and that you can import math.sqrt for square root.
import math
class Rocket:
...
def distance(self, other):
return math.sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2)
The above code only requires other to have x and y attributes, it does not need to be a Rocket instance.

How to cancel the last method called?

class Minobot:
def __init__(self):
self.x,self.y = 0, 0
self.angle = 90
def forward(self, d):
angle = radians(90-self.angle)
nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
self.x, self.y = nx, ny
def right(self):
self.angle += 90
def left(self):
self.angle -= 90
def coordinates(self):
return round(self.x, 1), round(self.y, 1)
def manhattan_distance(self):
return int(abs(round(self.x, 1))+abs(round(self.y, 1)))
def cancel(self):
????
Now I need to add another method to this class that cancels the last method called.
For example: a.forward(2) => a.right() => a.cancel() This would set the Minobot before the a.right() is used.
You cannot cancel the last action unless you store it.
If you store the last action, you can invert it. Since you know the reverse action once you take an action, you can just store the reverse action directly.
Let your Minibot instance have a .reverse_action attribute which is a tuple of the method to call and arguments to pass.
So
def left(self):
# Note how methods can just be stored in variables.
self.reverse_action = (self.right, ())
...
def forward(self, distance):
# Forward is its own reverse with a negative distance.
self.reverse_action = (self.forward, (-distance,))
def revert_last(self):
if self.reverse_action:
(method, args) = self.reverse_action
method(*args) # Call the stored method, passing stored args.
self.reverse_action = None # Used it up.
This has an obvious downside of being able to only revert one last action. If you store a list of reverse actions for each action you take, you can .pop() from it and revert actions as long as there are any stored reverse actions in the list.
You can store only last several actions if you're taking great many actions and are memory-constrained. (Terms to google up: "Undo buffer", "Circular buffer", "Event sourcing".)
Another approach would be storing the previous state, that is, coordinates, heading, etc. Undoing the last action would be then just switching to the previous state:
def save_state(self):
self.previous_state = (self.x, self.y, self.angle)
# Or: self.previous_states_list.append(...)
def restore_previous_state(self):
(self.x, self.y, self.angle) = self.previous_state
# Or: ... = self.previous_states_list.pop()
def left(self):
self.save_state()
...
This approach is free from rounding errors, etc. It takes more memory, though, especially as your state grows large, and when you want to save an entire history of previous states.
Rather than saving the reversing action as other answers have suggested, you can save all the properties, and cancel can restore them.
class Minobot:
def __init__(self):
self.x,self.y, self.oldx, self.oldy = 0, 0
self.angle, self.oldangle = 90
def forward(self, d):
self.oldx, self.oldy = self.x, self.y
angle = radians(90-self.angle)
nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
self.x, self.y = nx, ny
def right(self):
self.oldangle = self.angle
self.angle += 90
def left(self):
self.oldangle = self.angle
self.angle -= 90
def coordinates(self):
return round(self.x, 1), round(self.y, 1)
def manhattan_distance(self):
return int(abs(round(self.x, 1))+abs(round(self.y, 1)))
def cancel(self):
self.angle, self.x, self.y = self.oldangle, self.oldx, self.oldy
You can save the last action and run it in reverse:
(thank you to the guy that made me update with lambdas)
class Minobot:
def __init__(self):
self.x,self.y = 0, 0
self.angle = 90
def forward(self, d):
angle = radians(90-self.angle)
nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
self.x, self.y = nx, ny
self.undo = lambda:self.forward(-d)
def right(self):
self.angle += 90
self.undo = lambda:self.left()
def left(self):
self.angle -= 90
self.undo = lambda:self.right()
def coordinates(self):
return round(self.x, 1), round(self.y, 1)
def manhattan_distance(self):
return int(abs(round(self.x, 1))+abs(round(self.y, 1)))
def cancel(self):
self.undo()
Note: will cause a problem if you run cancel without something to cancel
You could try keeping a records of all previous states, but memory usage could grow to be larger than you want. In the solution shown below, the pickle protocol is used to get and set the state of the object as needed. Please note that if you pickle instances of the following class, the instance will not keep its history. The code will probably need to be adapted to your particular requirements.
#! /usr/bin/env python3
import math
class MinnowBot:
__slots__ = '__x', '__y', '__angle', '__history'
def __init__(self):
self.__x = 0
self.__y = 0
self.__angle = 90
def forward(self, distance):
self.history.append(self.__getstate__())
angle = math.radians(90 - self.__angle)
self.__x += distance * math.cos(angle)
self.__y += distance * math.sin(angle)
def right(self):
self.history.append(self.__getstate__())
self.__angle += 90
def left(self):
self.history.append(self.__getstate__())
self.__angle -= 90
#property
def coordinates(self):
return round(self.__x, 1), round(self.__y, 1)
#property
def manhattan_distance(self):
return round(abs(self.__x) + abs(self.__y))
def cancel(self):
self.__setstate__(self.history.pop())
def __getstate__(self):
return self.__x, self.__y, self.__angle
def __setstate__(self, state):
self.__x, self.__y, self.__angle = state
#property
def history(self):
try:
history = self.__history
except AttributeError:
# noinspection PyAttributeOutsideInit
history = self.__history = []
return history
With you short explain maybe you can add state attribute and:
class Minobot:
def __init__(self):
self.x,self.y = 0, 0
self.angle = 90
self.state = self.__init__
self.d = 0
def forward(self, d=None):
if d is None:
d = self.d
angle = radians(90-self.angle)
nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
self.x, self.y = nx, ny
self.d = d
self.state = self.forward
def right(self):
self.angle += 90
self.state = self.left
def left(self):
self.angle -= 90
self.state = self.right
def coordinates(self):
return round(self.x, 1), round(self.y, 1)
def manhattan_distance(self):
return int(abs(round(self.x, 1))+abs(round(self.y, 1)))
def cancel(self):
if self.state is None:
pass
else:
state = self.state
self.state = None
return state()
If memory is not a concern, you can store/restore state by saving/restoring the whole object.
I am also considering that control of the target object outside its class is not a problem. I assume the object is not cancelling the last action itself, but some other code controls that. Otherwise you'll have to manage your class relation with the Stack class.
In the below example I use deepcopy, but pickle.dumps can also be used.
I choose to use a stack like structure, which allows the saving of whatever number of objects/states you want. But replacing with a rotating buffer would allow the storage of only the last n objects/states.
The advantage I see over some of the other answers is not worrying about what methods were called or attributes had changed. It's also simple. Preserving states and frames/environments always remembers me of dicts and stacks:
from copy import deepcopy
class ObjectStack():
def __init__(self):
self.objects = []
def push(self, obj):
self.objects.append(deepcopy(obj))
def pop(self):
return self.objects.pop() if self.objects else None
#just an example class, not OP's original class
class char():
def __init__(self, char):
self.char = char
stack = ObjectStack()
c = char('C')
stack.push(c) # save object
c = char('D') # somehow object changes
print(c.char)
last = stack.pop() # restore last object/state
if last is not None:
c = last
print(c.char)
last = stack.pop()
if last is None:
print('No more objects/states saved')

Calling a method from parent class that has a different name in the subclass

Having the following code:
class Point:
'class that represents a point in the plane'
def __init__(self, xcoord=0, ycoord=0):
''' (Point,number, number) -> None
initialize point coordinates to (xcoord, ycoord)'''
self.x = xcoord
self.y = ycoord
def setx(self, xcoord):
''' (Point,number)->None
Sets x coordinate of point to xcoord'''
self.x = xcoord
def sety(self, ycoord):
''' (Point,number)->None
Sets y coordinate of point to ycoord'''
self.y = ycoord
def get(self):
'''(Point)->tuple
Returns a tuple with x and y coordinates of the point'''
return (self.x, self.y)
def move(self, dx, dy):
'''(Point,number,number)->None
changes the x and y coordinates by dx and dy'''
self.x += dx
self.y += dy
def __repr__(self):
'''(Point)->str
Returns canonical string representation Point(x, y)'''
return 'Point('+str(self.x)+','+str(self.y)+')'
class Rectangle(Point):
def __init__(self,bottom_left,top_right,color):
self.get = bottom_left
self.get = top_right
self.color = color
def get_bottom_left(self,bottom_left):
print ()
r1 = Rectangle(Point(0,0), Point(1,1), "red")
r1.get_bottom_left()
I want to be able to print "Point(0,0)" by calling self__rep__(self) from class Point from the method get_bottom_left, but I just have no idea how. I know how to use inheritance if the functions have the same name, but in this case I am stuck and it is a requirement for the child function to have the method names it has. If it looks that I am just looking for the answer, I would like the response to just explain me a similar case of this application please!
When I do the following:
class Rectangle(Point):
def __init__(self,bottom_left,top_right,color):
self.get = bottom_left
self.get = top_right
self.color = color
def get_bottom_left(self,bottom_left):
print (self.bottom_left)
I get: get_bottom_left() missing 1 required positional argument: 'bottom_left'
As mentioned in the comment, Rectangle should contain Point instances and not inherit Point. If you change Rectangle class as shown below, you'll see the expected result:
class Rectangle():
def __init__(self, bottom_left, top_right, color):
self.bottom_left = bottom_left
self.top_right = top_right
self.color = color
def get_bottom_left(self):
print self.bottom_left

Move points and check specific start value, using classes python

I need to be able to move points and check a specific point value. This is the code:
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def move(self)
#Here I want to move my points
Next class is a linestring. It must be able to handle x set of points
class LineString(Point):
def __init__(self, *points):
self.points = []
for point in points:
if not isinstance(point, Point):
point = Point(*point)
self.points.append(point)
def __getitem__(self):
#Here I want to inspect the value of the specific
# e.g. y value for the start point after it has been moved
I'm a bit unsure of how to get the __getitem__ to work and whether it's in the right position. Should it be under class Point? Could this be done in another way?
Edited code;
from numpy import sqrt
import math
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def dist(self, point):
return math.hypot(self.x - point.x, self.y - point.y)
def move(self, dx, dy):
self.x = self.x + dx
self.y = self.y + dy
class LineString(Point):
def __init__(self, *points):
self.points = []
for point in points:
if not isinstance(point, Point):
point = Point(*point)
self.points.append(point)
def length(self):
return sum(p1.dist(p2) for p1, p2 in zip(self.points[1:], self.points[:-1]))
def move (self, x, y):
for p in self.points:
p.move(x, y)
def __getitem__(self, key):
return self.points[key]
I think this is roughly what you want:
You don't seem to actually need a dictionary (for a line, I think a list makes more sense anyway). So the Line class is just a list of Points, and it provides a move_all_points function to translate them all. Because Line subclasses a list, you get all the standard behaviour of lists for free:
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return "<Point({},{})>".format(self.x, self.y)
def __str__(self):
return(repr(self))
def move(self, dx, dy):
"""Move the point by (dx, dy)."""
self.x += dx
self.y += dy
class Line(list):
"""A list of points that make up a line."""
def move_all_points(self, dx, dy):
for p in self:
p.move(dx, dy)
So then you can use them as follows:
>>> p1, p2, p3 = Point(0, 0), Point(5, 0), Point(10, 10)
>>> my_line = Line((p1, p2, ))
>>> print my_line
[<Point(0,0)>, <Point(5,0)>]
>>> my_line.append(p3)
>>> print my_line
[<Point(0,0)>, <Point(5,0)>, <Point(10,10)>]
>>> p4 = Point(100,100)
>>> my_line.move_all_points(1, 1)
>>> print my_line
[<Point(1,1)>, <Point(6,1)>, <Point(11,11)>]
>>> my_line.append(p4)
>>> print my_line
[<Point(1,1)>, <Point(6,1)>, <Point(11,11)>, <Point(100,100)>]

Categories