2 # Experimental implementation of a Python-based Tennix AI Bot
14 class BasicBot(object):
18 # FIXME: Expose debugging flag through "tennix" module
21 print self
.name
, 'says:', (' '.join(str(x
) for x
in args
))
23 class DumbPythonBot(BasicBot
):
24 name
= 'John McInpyre'
27 DESIRED_POWER
= { tennix
.INPUT_KEY_HIT
: 90, tennix
.INPUT_KEY_TOPSPIN
: 70, tennix
.INPUT_KEY_SMASH
: 70 }
28 DEFAULT_APPROACH_DISTANCE
= 40.0
30 def __init__(self
, gamestate
, player_id
):
31 self
.gamestate
= gamestate
32 self
.player_id
= player_id
36 self
.ball_approaching
= False
37 self
.have_to_serve
= False
39 self
.old_ball_x
, self
.old_ball_y
= tennix
.get_ball_pos(self
.gamestate
)
40 self
.ball_x
, self
.ball_y
= self
.old_ball_x
, self
.old_ball_y
41 self
.player_x
, self
.player_y
= tennix
.get_position(self
.gamestate
, self
.player_id
)
42 self
.power
= tennix
.get_power(self
.gamestate
, self
.player_id
)
44 self
.desired_y_position
= self
.GAME_Y_MID
45 self
.approach_distance
= self
.DEFAULT_APPROACH_DISTANCE
46 self
.think_about_y_position()
48 self
.cancelled
= False
49 self
.t
= threading
.Thread(target
=self
.think
)
51 self
.say(self
.name
, 'has been loaded')
53 def distance(self
, a
, b
):
54 return math
.sqrt((a
[0]-b
[0])**2 + (a
[1]-b
[1])**2)
56 def look_at_power_bar(self
):
57 self
.power
= tennix
.get_power(self
.gamestate
, self
.player_id
)
59 def look_at_player(self
):
60 self
.player_x
, self
.player_y
= tennix
.get_position(self
.gamestate
, self
.player_id
)
62 def look_at_ball(self
):
63 self
.old_ball_x
, self
.old_ball_y
= self
.ball_x
, self
.ball_y
64 self
.ball_x
, self
.ball_y
= tennix
.get_ball_pos(self
.gamestate
)
65 old_distance
= self
.distance((self
.player_x
, self
.player_y
), (self
.old_ball_x
, self
.old_ball_y
))
66 new_distance
= self
.distance((self
.player_x
, self
.player_y
), (self
.ball_x
, self
.ball_y
))
68 self
.ball_approaching
= (old_distance
> new_distance
)
69 self
.have_to_serve
= ((old_distance
== new_distance
) and abs(self
.player_x
- self
.ball_x
) < 50)
71 def have_sufficient_power(self
):
72 return self
.power
> 90
74 def think_about_y_position(self
):
75 if not self
.ball_approaching
and not self
.have_to_serve
:
76 #self.desired_y_position = self.GAME_Y_MID
79 ball_x_speed
= self
.old_ball_x
- self
.ball_x
81 ball_y_speed
= self
.old_ball_y
- self
.ball_y
83 #self.say('My ball x speed is', ball_x_speed)
84 #self.say('My ball y speed is', ball_y_speed)
86 x_diff
= self
.player_x
- self
.ball_x
87 if ball_x_speed
!= 0.0:
88 self
.approach_distance
= abs(ball_x_speed
*.6)
89 y_diff
= ball_y_speed
* x_diff
/ ball_x_speed
91 self
.approach_distance
= self
.DEFAULT_APPROACH_DISTANCE
94 self
.say('I have to move', y_diff
, 'to catch the ball now', x_diff
, 'away')
96 self
.desired_y_position
= self
.ball_y
+ y_diff
+ random
.randint(-5,5)
98 def am_positioned(self
):
99 return abs(self
.desired_y_position
- self
.player_y
) < 30
103 while not self
.cancelled
:
106 self
.look_at_power_bar()
107 self
.look_at_player()
110 self
.think_about_y_position()
112 if self
.pressed_key
== -1:
113 if self
.ball_approaching
or self
.have_to_serve
:
114 self
.pressed_key
= tennix
.INPUT_KEY_HIT
116 self
.say('holding key with distance', abs(self
.player_x
- self
.ball_x
))
117 self
.say('my approach distance is', self
.approach_distance
)
118 if not self
.ball_approaching
and not self
.have_to_serve
:
119 self
.pressed_key
= -1
120 if abs(self
.player_x
- self
.ball_x
) < self
.approach_distance
and self
.have_sufficient_power() and self
.am_positioned():
121 self
.pressed_key
= -1
123 #self.current_tactic = random.randint(tennix.INPUT_KEY_HIT, tennix.INPUT_KEY_SMASH)
124 #self.desired_power = self.DESIRED_POWER[self.current_tactic]*(1.2-0.4*random.random())
125 #print 'determined new tactic:', self.current_tactic
127 self
.say('Thread for', self
.name
, 'finished')
129 # if math.sqrt((self.player_x - self.ball_x)**2 + (self.player_y - self.ball_y)**2) > 150:
132 # if self.power > self.desired_power and random.randint(0, 3)==0:
133 # self.power_locked = True
135 # if key_id == self.current_tactic and not self.power_locked:
136 # #if abs(self.player_x - self.ball_x) < 10 and self.power > self.desired_power:
137 # return abs(self.player_x - self.ball_x) > 5 and self.power < self.desired_power + 0.3*random.random()
142 self
.say('I am going to be unloaded')
143 self
.cancelled
= True
144 if self
.t
is not None:
147 def get_key(self
, key_id
):
148 return self
.pressed_key
== key_id
150 def get_axis(self
, axis_id
):
151 self
.look_at_player()
152 if axis_id
== tennix
.INPUT_AXIS_Y
:
153 return max(-1.0, min(1.0, (self
.desired_y_position
- self
.player_y
)/4.))
158 tennix
.register_bot(DumbPythonBot
)