Tennix 1.1 "Classic Championship Tour 2011" released
[tennix.git] / defaultbot.py
blob3a5f232e684ef86d8f96717ec2dc41f118f7a0e7
2 # Experimental implementation of a Python-based Tennix AI Bot
4 import random
5 import time
6 import threading
7 import math
8 import sys
10 import tennix
12 DEBUG = False
14 class BasicBot(object):
15 name = 'Unnamed Bot'
17 def say(self, *args):
18 # FIXME: Expose debugging flag through "tennix" module
19 global DEBUG
20 if DEBUG:
21 print self.name, 'says:', (' '.join(str(x) for x in args))
23 class DumbPythonBot(BasicBot):
24 name = 'John McInpyre'
26 GAME_Y_MID = 242
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
34 self.pressed_key = -1
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)
50 self.t.start()
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
77 return
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
90 else:
91 self.approach_distance = self.DEFAULT_APPROACH_DISTANCE
92 y_diff = 0.0
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
101 def think(self):
102 i = 0
103 while not self.cancelled:
104 time.sleep(.2)
106 self.look_at_power_bar()
107 self.look_at_player()
108 self.look_at_ball()
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
115 else:
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
126 i += 1
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:
130 # return False
132 # if self.power > self.desired_power and random.randint(0, 3)==0:
133 # self.power_locked = True
134 # return False
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()
138 # else:
139 # return False
141 def finish(self):
142 self.say('I am going to be unloaded')
143 self.cancelled = True
144 if self.t is not None:
145 self.t.join()
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.))
155 return 0.0
158 tennix.register_bot(DumbPythonBot)