| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- '''
- @author: olivier.massot, 2019
- '''
- import heapq
- import sys
- DEBUG = True
- def log(x):
- if DEBUG:
- print(x, file=sys.stderr)
- # Cells
- START_0 = '0'
- START_1 = '1'
- BLUEBERRIES_CRATE = "B"
- ICE_CREAM_CRATE = "I"
- STRAWBERRIES_CRATE = "S"
- CHOPPING_BOARD = "C"
- DOUGH_CRATE = "H"
- WINDOW = "W"
- EMPTY_TABLE = "#"
- DISHWASHER = "D"
- FLOOR_CELL = "."
- OVEN = "O"
- # Items
- NONE = "NONE"
- DISH = "DISH"
- ICE_CREAM = "ICE_CREAM"
- BLUEBERRIES = "BLUEBERRIES"
- STRAWBERRIES = "STRAWBERRIES"
- CHOPPED_STRAWBERRIES = "CHOPPED_STRAWBERRIES"
- DOUGH = "DOUGH"
- CROISSANT = "CROISSANT"
- location = {DISH: DISHWASHER,
- ICE_CREAM: ICE_CREAM_CRATE,
- BLUEBERRIES: BLUEBERRIES_CRATE,
- STRAWBERRIES: STRAWBERRIES_CRATE,
- CHOPPED_STRAWBERRIES: CHOPPING_BOARD,
- CROISSANT: OVEN,
- DOUGH: DOUGH_CRATE,
- OVEN: OVEN,
- WINDOW: WINDOW}
- special = list(location.values())
- preq = {CHOPPED_STRAWBERRIES: STRAWBERRIES,
- CROISSANT: DOUGH}
- needs_preparation = {STRAWBERRIES: CHOPPED_STRAWBERRIES,
- DOUGH: CROISSANT}
- class Base():
- def __repr__(self):
- return f"<{self.__class__.__name__}: {self.__dict__}>"
- class Customer(Base):
- def __init__(self, item, award):
- self.item = item
- self.award = int(award)
- class Cook(Base):
- def __init__(self, x, y, item):
- self.x = int(x)
- self.y = int(y)
- self.item = item
- self.order = []
- @property
- def pos(self):
- return (self.x, self.y)
- def update(self, x, y, item):
- self.x = int(x)
- self.y = int(y)
- self.item = item
-
- def use(self, x, y, msg=""):
- print("USE", x, y, msg)
- def move(self, x, y):
- print("MOVE", x, y)
-
- def wait(self):
- print("WAIT")
-
- class Table(Base):
- def __init__(self, x, y, item):
- self.x = int(x)
- self.y = int(y)
- self.item = item
-
- class Oven(Base):
- def __init__(self, contents, timer):
- self.contents = contents
- self.timer = int(timer)
- class PathNode(tuple):
- def __new__(self, x, y, parent=None):
- n = tuple.__new__(self, (x, y))
- n.parent = parent
- n.cost = 0
- return n
- class Grid(Base):
- def __init__(self, cells):
- self.cells = cells
- self.w, self.h = len(cells[0]), len(cells)
- self.add_costs = {}
- def at(self, x, y):
- return self.cells[y][x]
- def flatten(self):
- return [(x, y, c) for y, row in enumerate(self.cells) for x, c in enumerate(row)]
-
- @property
- def coords(self):
- return [(x, y) for y in range(self.h) for x in range(self.w)]
-
- def where_are(self, content):
- return [(x, y) for x, y, c in self.flatten() if c == content]
- @staticmethod
- def distance(from_, to_):
- return abs(from_[0] - to_[0]) + abs(from_[1] - to_[1])
- def closest(self, from_, content):
- return sorted([(c, Grid.distance(from_, c)) for c in self.where_are(content)], key=lambda k: k[1])[0]
- def neighbors(self, x, y, diags=True):
- neighs = [(x, y - 1), (x - 1, y), (x + 1, y), (x, y + 1)]
- if diags:
- neighs += [(x - 1, y - 1), (x + 1, y - 1), (x - 1, y + 1), (x + 1, y + 1)]
- return [(x, y) for x, y in neighs if 0 <= x < self.w and 0 <= y < self.h]
- def passable(self, x, y):
- return self.at(x, y) in (FLOOR_CELL, START_0, START_1)
-
- def cost(self, x, y):
- return 10 + self.add_costs.get((x, y), 0)
-
- def path(self, origin, target, incl_start=False):
- nodes = []
- origin = PathNode(*origin)
- targets = grid.neighbors(*target)
- heapq.heappush(nodes, (0, origin))
-
- while nodes:
- current = heapq.heappop(nodes)[1]
- if current in targets:
- path = []
- next_ = current
- while next_:
- if next_ != origin or incl_start:
- path.insert(0, next_)
- next_ = next_.parent
- return path
-
- neighbors = self.neighbors(*current, False)
- for x, y in neighbors:
-
- if not self.passable(x, y):
- continue
- cost = current.cost + self.cost(x, y)
- priority = cost + 10 * (abs(x - target[0]) + abs(y - target[1]))
- node = PathNode(x, y, current)
- node.cost = cost
- heapq.heappush(nodes, (priority, node))
- else:
- return None
- # input vars
- num_all_customers = int(input())
- all_customers = [Customer(*input().split()) for _ in range(num_all_customers)]
- grid = Grid([list(input()) for i in range(7)])
- log(f"{num_all_customers} customers: {all_customers}")
- log(f"grid: {grid}")
- class Task(Base):
- def __init__(self, name, from_):
- self.name = name
- self.loc = location[name]
- self.pos, self.dist = grid.closest(from_, self.loc)
- player = Cook(-1, -1, "")
- partner = Cook(-1, -1, "")
- class Action(Base):
- name = ""
- needs_free_hands = False
- where = ""
- NOT_STARTED = 0
- RUNNING = 1
- ENDED = 2
-
- class Preparation(Base):
- name = ""
- steps = []
- oven_time = 0
-
- def __init__(self):
- self.pending = self.steps
- self.cooked = False
-
- @property
- def complete(self):
- return not self.pending
-
- @property
- def available(self):
- return not self.oven_time or self.cooked
-
- def whats_next(self):
- return self.pending.pop(0)
-
- class Bluberries(Preparation):
- name = BLUEBERRIES
- steps = [BLUEBERRIES_CRATE]
-
- class Icecream(Preparation):
- name = ICE_CREAM
- steps = [ICE_CREAM_CRATE]
-
- class ChoppedStrawberries(Preparation):
- name = CHOPPED_STRAWBERRIES
- steps = [STRAWBERRIES_CRATE, CHOPPING_BOARD]
-
- class Croissant(Preparation):
- name = CROISSANT
- steps = [DOUGH_CRATE, OVEN]
- oven_time = 10
- preparations = {BLUEBERRIES: Bluberries,
- ICE_CREAM: Icecream,
- CHOPPED_STRAWBERRIES: ChoppedStrawberries,
- CROISSANT: Croissant}
- class Order():
- def __init__(self, order):
- self.order = [preparations[o] for o in order]
-
- def complete(self):
- return all(k for k in self.order in player.item)
-
- def update(self, player_item):
- for p in self.order:
- if p.name in player_item:
- p.complete = True
-
- def on_dish(self):
- return DISH in player.item
-
- def pending(self):
- if self.complete:
- return [WINDOW]
-
-
-
- return [p.pending[0] for p in self.pending if p.available and not p.complete]
- while True:
- turns_remaining = int(input())
- player.update(*input().split())
- log(f"*** player: {player}")
-
- partner.update(*input().split())
- log(f"*** partner: {partner}")
-
- num_tables_with_items = int(input()) # the number of tables in the kitchen that currently hold an item
- tables = [Table(*input().split()) for _ in range(num_tables_with_items)]
- log(f"*** tables: {tables}")
-
- oven = Oven(*input().split())
- log(f"*** oven: {oven}")
-
- num_customers = int(input()) # the number of customers currently waiting for food
- customers = [Customer(*input().split()) for _ in range(num_customers)]
- log(f"*** customers: {customers}")
-
- ### SCRIPT
-
- # if no current order, take the most beneficial
- if not player.order:
- queue = sorted(customers, reverse=True, key=lambda x: x.award)
- player.order = queue[0].item.split('-')
- log(f'>>> new order taken: {player.order}')
- todo = [Task(t, player.pos) for t in player.order if t not in player.item]
- log(f"todo: {todo}")
- if not todo:
- # no task left, target is the window
- next_task = Task(WINDOW, player.pos)
- player.order, todo = [], []
-
- elif player.item in needs_preparation:
- # cook has an ingredient in hands, he needs to prepare it
- next_task = Task(needs_preparation[player.item], player.pos)
-
- elif any((t.name in preq for t in todo)) and not DISH in player.item:
- # hands shall be free to handle ingredients, we go for it first if possible
- next_task = Task(next((preq[t.name] for t in todo if t.name in preq)), player.pos)
-
- elif oven.timer < 1:
- # oven has dinged
- next_task = Task(OVEN, player.pos)
-
- elif player.item != NONE and not DISH in player.item:
- # cook has something in its hands and no dish, he have to take one
- next_task = next((t for t in todo if t.name == DISH))
-
- else:
- # else, go for the closest task
- tasks = sorted(todo, key= lambda x: x.dist)
- next_task = next(iter(tasks), None)
-
- log(f"next_task: {next_task}")
-
- # update grid movement costs following the probability of finding the partner here
- partner_could_be_there = [(x, y) for x, y in grid.coords if grid.passable(x, y) and grid.distance(partner.pos, (x, y)) <= 4]
- grid.add_costs = {}
- for x, y in partner_could_be_there:
- k1 = 2 if (x, y) == partner.pos else 1
- # cell is next to a special cell, partner has more chance to stop there
- k2 = 2 if any((c for c in grid.neighbors(x, y) if grid.at(*c) in special )) else 1
-
- grid.add_costs[(x, y)] = k1 * k2 * 3
- log(grid.add_costs)
-
- path = grid.path(player.pos, next_task.pos)
- log(path)
-
- if path is not None:
- if len(path) > 0:
- if len(path) > 4:
- player.move(*path[3])
- else:
- player.move(*path[-1])
- else:
- player.use(*next_task.pos)
- else:
- player.use(*next_task.pos)
|