sys.path.append('../')
import game_common
import server_.map_
+import server_.io
+import server_.tasks
+from server_.game_error import GameError
from parser import ArgError
-class GameError(Exception):
- pass
-
-
class World(game_common.World):
def __init__(self, game):
self.things = [player, npc]
-class Task:
-
- def __init__(self, thing, name, args=()):
- self.name = name
- self.thing = thing
- self.args = args
- self.todo = 3
+ return 'success'
- def check(self):
- if self.name == 'MOVE':
- test_pos = self.thing.world.map_.move(self.thing.position, self.args[0])
- if self.thing.world.map_[test_pos] != '.':
- raise GameError(str(self.thing.id_) +
- ' would move into illegal terrain')
- for t in self.thing.world.things:
- if t.position == test_pos:
- raise GameError(str(self.thing.id_) +
- ' would move into other thing')
class Thing(game_common.Thing):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.task = Task(self, 'WAIT')
- self.last_task_result = None
+ self.task = self.world.game.task_manager.get_task_class('WAIT')(self)
+ self._last_task_result = None
self._stencil = None
- def task_WAIT(self):
- return 'success'
-
- def task_MOVE(self, direction):
- self.position = self.world.map_.move(self.position, direction)
- return 'success'
- task_MOVE.argtypes = 'string:direction'
-
def move_towards_target(self, target):
dijkstra_map = type(self.world.map_)(self.world.map_.size)
n_max = 256
dijkstra_map.terrain = [n_max for i in range(dijkstra_map.size_i)]
dijkstra_map[target] = 0
shrunk = True
+ visible_map = self.get_visible_map()
while shrunk:
shrunk = False
for pos in dijkstra_map:
- if self.world.map_[pos] != '.':
+ if visible_map[pos] != '.':
continue
- neighbors = dijkstra_map.get_neighbors(pos)
- for yx in neighbors:
+ neighbors = dijkstra_map.get_neighbors(tuple(pos))
+ for direction in neighbors:
+ yx = neighbors[direction]
if yx is not None and dijkstra_map[yx] < dijkstra_map[pos] - 1:
dijkstra_map[pos] = dijkstra_map[yx] + 1
shrunk = True
# else:
# f.write('~')
# f.write('\n')
- neighbors = dijkstra_map.get_neighbors(self.position)
+ neighbors = dijkstra_map.get_neighbors(tuple(self.position))
n = n_max
- dirs = dijkstra_map.get_directions()
+ #print('DEBUG', self.position, neighbors)
+ #dirs = dijkstra_map.get_directions()
#print('DEBUG dirs', dirs)
#print('DEBUG neighbors', neighbors)
#debug_scores = []
# else:
# debug_scores += [dijkstra_map[pos]]
#print('DEBUG debug_scores', debug_scores)
- direction = None
- for i_dir in range(len(neighbors)):
- pos = neighbors[i_dir]
- if pos is not None and dijkstra_map[pos] < n:
- n = dijkstra_map[pos]
- direction = dirs[i_dir]
+ target_direction = None
+ for direction in neighbors:
+ yx = neighbors[direction]
+ if yx is not None:
+ n_new = dijkstra_map[yx]
+ if n_new < n:
+ n = n_new
+ target_direction = direction
#print('DEBUG result', direction)
- if direction:
- self.set_task('MOVE', (direction,))
- #self.world.game.io.send('would move ' + direction)
+ if target_direction:
+ self.set_task('MOVE', (target_direction,))
def decide_task(self):
+ # TODO: Check if monster can follow player too well (even when they should lose them)
visible_things = self.get_visible_things()
target = None
for t in visible_things:
def set_task(self, task_name, args=()):
- self.task = Task(self, task_name, args)
+ task_class = self.world.game.task_manager.get_task_class(task_name)
+ self.task = task_class(self, args)
self.task.check() # will throw GameError if necessary
def proceed(self, is_AI=True):
self.task.check()
except GameError as e:
self.task = None
- self.last_task_result = e
+ self._last_task_result = e
if is_AI:
try:
self.decide_task()
return
self.task.todo -= 1
if self.task.todo <= 0:
- task = getattr(self, 'task_' + self.task.name)
- self.last_task_result = task(*self.task.args)
+ self._last_task_result = self.task.do()
self.task = None
if is_AI and self.task is None:
try:
class Game(game_common.CommonCommandsMixin):
def __init__(self, game_file_name):
- import server_.io
self.map_manager = server_.map_.map_manager
+ self.task_manager = server_.tasks.task_manager
self.world = World(self)
self.io = server_.io.GameIO(game_file_name, self)
# self.pool and self.pool_result are currently only needed by the FIB
def send_gamestate(self, connection_id=None):
"""Send out game state data relevant to clients."""
- def stringify_yx(tuple_):
- """Transform tuple (y,x) into string 'Y:'+str(y)+',X:'+str(x)."""
- return 'Y:' + str(tuple_[0]) + ',X:' + str(tuple_[1])
-
- self.io.send('NEW_TURN ' + str(self.world.turn))
+ self.io.send('TURN ' + str(self.world.turn))
self.io.send('MAP ' + self.world.map_.geometry +\
- ' ' + stringify_yx(self.world.map_.size))
+ ' ' + server_.io.stringify_yx(self.world.map_.size))
visible_map = self.world.get_player().get_visible_map()
for y, line in visible_map.lines():
- self.io.send('VISIBLE_MAP_LINE %5s %s' % (y, self.io.quote(line)))
+ self.io.send('VISIBLE_MAP_LINE %5s %s' % (y, server_.io.quote(line)))
visible_things = self.world.get_player().get_visible_things()
for thing in visible_things:
self.io.send('THING_TYPE %s %s' % (thing.id_, thing.type_))
self.io.send('THING_POS %s %s' % (thing.id_,
- stringify_yx(thing.position)))
+ server_.io.stringify_yx(thing.position)))
player = self.world.get_player()
- self.io.send('PLAYER_POS %s' % (stringify_yx(player.position)))
+ self.io.send('PLAYER_POS %s' % (server_.io.stringify_yx(player.position)))
self.io.send('GAME_STATE_COMPLETE')
def proceed(self):
"""
self.io.send('TURN_FINISHED ' + str(self.world.turn))
self.world.proceed_to_next_player_turn()
- msg = str(self.world.get_player().last_task_result)
- self.io.send('LAST_PLAYER_TASK_RESULT ' + self.io.quote(msg))
+ msg = str(self.world.get_player()._last_task_result)
+ self.io.send('LAST_PLAYER_TASK_RESULT ' + server_.io.quote(msg))
self.send_gamestate()
def cmd_FIB(self, numbers, connection_id):
def cmd_INC_P(self, connection_id):
"""Increment world.turn, send game turn data to everyone.
- To simulate game processing waiting times, a one second delay between
- TURN_FINISHED and NEW_TURN occurs; after NEW_TURN, some expensive
- calculations are started as pool processes that need to be finished
- until a further INC finishes the turn.
+ To simulate game processing waiting times, a one second delay
+ between TURN_FINISHED and TURN occurs; after TURN, some
+ expensive calculations are started as pool processes that need
+ to be finished until a further INC finishes the turn.
+
+ This is just a demo structure for how the game loop could work
+ when parallelized. One might imagine a two-step game turn,
+ with a non-action step determining actor tasks (the AI
+ determinations would take the place of the fib calculations
+ here), and an action step wherein these tasks are performed
+ (where now sleep(1) is).
- This is just a demo structure for how the game loop could work when
- parallelized. One might imagine a two-step game turn, with a non-action
- step determining actor tasks (the AI determinations would take the
- place of the fib calculations here), and an action step wherein these
- tasks are performed (where now sleep(1) is).
"""
from time import sleep
if self.pool_result is not None:
self.world.get_player().set_task(task_name, args)
self.proceed()
- method = None
- argtypes = ''
- task_prefix = 'TASK:'
- if command_name[:len(task_prefix)] == task_prefix:
- task_name = command_name[len(task_prefix):]
- task_method_candidate = 'task_' + task_name
- if hasattr(Thing, task_method_candidate):
- method = partial(cmd_TASK_colon, task_name)
- task_method = getattr(Thing, task_method_candidate)
- if hasattr(task_method, 'argtypes'):
- argtypes = task_method.argtypes
- return method, argtypes
- method_candidate = 'cmd_' + command_name
- if hasattr(self, method_candidate):
- method = getattr(self, method_candidate)
- if hasattr(method, 'argtypes'):
- argtypes = method.argtypes
- return method, argtypes
+ def cmd_SET_TASK_colon(task_name, thing_id, todo, *args):
+ t = self.world.get_thing(thing_id, False)
+ if t is None:
+ raiseArgError('No such Thing.')
+ task_class = self.task_manager.get_task_class(task_name)
+ t.task = task_class(t, args)
+ t.task.todo = todo
+
+ def task_prefixed(command_name, task_prefix, task_command,
+ argtypes_prefix=''):
+ func = None
+ argtypes = ''
+ if command_name[:len(task_prefix)] == task_prefix:
+ task_name = command_name[len(task_prefix):]
+ task_manager_reply = self.task_manager.get_task_class(task_name)
+ if task_manager_reply is not None:
+ func = partial(task_command, task_name)
+ task_class = task_manager_reply
+ argtypes = task_class.argtypes
+ if func is not None:
+ return func, argtypes_prefix + argtypes
+ return None, argtypes
+
+ func, argtypes = task_prefixed(command_name, 'TASK:', cmd_TASK_colon)
+ if func:
+ return func, argtypes
+ func, argtypes = task_prefixed(command_name, 'SET_TASK:',
+ cmd_SET_TASK_colon,
+ 'int:nonneg int:nonneg ')
+ if func:
+ return func, argtypes
+ func_candidate = 'cmd_' + command_name
+ if hasattr(self, func_candidate):
+ func = getattr(self, func_candidate)
+ if hasattr(func, 'argtypes'):
+ argtypes = func.argtypes
+ return func, argtypes
def get_string_options(self, string_option_type):
if string_option_type == 'geometry':
elif string_option_type == 'direction':
return self.world.map_.get_directions()
return None
+
+ def cmd_PLAYER_ID(self, id_):
+ # TODO: test whether valid thing ID
+ self.world.player_id = id_
+ cmd_PLAYER_ID.argtypes = 'int:nonneg'
+
+ def cmd_TURN(self, n):
+ self.world.turn = n
+ cmd_TURN.argtypes = 'int:nonneg'
+
+ def cmd_SAVE(self):
+
+ def write(f, msg):
+ f.write(msg + '\n')
+
+ save_file_name = self.io.game_file_name + '.save'
+ with open(save_file_name, 'w') as f:
+ write(f, 'TURN %s' % self.world.turn)
+ write(f, 'MAP ' + self.world.map_.geometry + ' ' + server_.io.stringify_yx(self.world.map_.size))
+ for y, line in self.world.map_.lines():
+ write(f, 'TERRAIN_LINE %5s %s' % (y, server_.io.quote(line)))
+ for thing in self.world.things:
+ write(f, 'THING_TYPE %s %s' % (thing.id_, thing.type_))
+ write(f, 'THING_POS %s %s' % (thing.id_,
+ server_.io.stringify_yx(thing.position)))
+ task = thing.task
+ if task is not None:
+ task_args = task.get_args_string()
+ write(f, 'SET_TASK:%s %s %s %s' % (task.name, thing.id_,
+ task.todo, task_args))
+ write(f, 'PLAYER_ID %s' % self.world.player_id)
+ cmd_SAVE.dont_save = True