Skip to content
Snippets Groups Projects
Verified Commit ac22c1cb authored by Maarten van den Berg's avatar Maarten van den Berg
Browse files

Add simple greedy online solver

parent d4759bcc
No related branches found
No related tags found
No related merge requests found
......@@ -10,6 +10,7 @@ from cinematinator.instance_io import (
parse_group_counts,
prettyprint_cinema,
)
from cinematinator.online_test import TestOnlineSolver
from cinematinator.offline import BruteforceOfflineSolver
from cinematinator.mip_test import MIPSolver
from cinematinator.simple_cinema import SimpleCinema
......@@ -44,7 +45,7 @@ class OfflineSolvers(enum.Enum):
@click.option(
"--solver",
"solver_name",
type=click.Choice(OfflineSolvers._member_names_),
type=click.Choice(OfflineSolvers._member_names_), # type:ignore
default="mip",
)
@click.argument("input_file", type=click.File(mode="r"))
......@@ -82,5 +83,35 @@ def edge_algo_cmd(input_file: TextIO) -> None:
print(result)
@cli.command("online")
@click.argument("input_file", type=click.File(mode="r"))
def online_cmd(input_file: TextIO) -> None:
"""
Online dingos
"""
cinema = parse_cinema(input_file)
solver = TestOnlineSolver(cinema)
for line in input_file:
input = line.strip()
group_size = int(input)
if group_size == 0:
print(solver.cinema.get_amount_people_seated())
else:
maybe_location = solver.try_seat_group(group_size)
if maybe_location is None:
print("0 0")
else:
x, y = maybe_location
print(f"{x+1} {y+1}")
if __name__ == "__main__":
cli()
from __future__ import annotations
from typing import Optional, Tuple, List
from cinematinator.types import Cinema, Coordinates
class TestOnlineSolver:
def __init__(self, cinema: Cinema) -> None:
self.cinema = cinema
def try_seat_group(self, group_size: int) -> Optional[Coordinates]:
potential_locations: List[Tuple[int, Coordinates]] = []
for y in range(self.cinema.height):
potential_locations.extend(scan_row(self.cinema, group_size, y))
potential_locations.sort()
if len(potential_locations) == 0:
return None
penalty, optimal_location = potential_locations[0]
seat_at_x, seat_at_y = optimal_location
self.cinema.seat_group(seat_at_x, seat_at_y, group_size)
return optimal_location
def scan_row(cinema: Cinema, group_size: int, y: int) -> List[Tuple[int, Coordinates]]:
"""
Scan the row at the given height for open spots that fit the given group size, then
return the starting location that fits the group while blocking the smallest amount
of still-available seats.
If there are multiple eligible locations that block the same number of
still-available seats, return the leftmost position.
If there is such a spot, return a 2-tuple (blocked_seats, x).
If there is no such spot, return None.
"""
potential_locations: List[Tuple[int, Coordinates]] = []
# Optimalization: we will never be able to seat a group at a position less than
# group_size from the right edge.
for x in range(0, cinema.width - group_size + 1):
if cinema.can_seat_group(x, y, group_size):
penalty = cinema.newly_blocked_seats(x, y, group_size)
potential_locations.append((penalty, (x, y)))
return potential_locations
......@@ -151,6 +151,36 @@ class Cinema:
return amount
def newly_blocked_seats(self, x: int, y: int, group_size: int) -> int:
"""
Return the amount of currently available seats that would become blocked if a
group were to be seated at this location.
"""
result: int = 0
for check_y in (y - 1, y + 1):
if check_y not in range(0, self.height):
continue
start_x = max(x - 1, 0)
end_x = min(x + 1, self.width) + 1
for check_x in range(start_x, end_x + 1):
if self[check_x, check_y] == SeatStatus.Available:
result += 1
for check_x in (x - 2, x - 1, x + group_size + 1, x + group_size + 2):
if check_x not in range(0, self.width):
continue
if self[check_x, y] == SeatStatus.Available:
result += 1
return result
class OfflineSolverProtocol(Protocol):
"""
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment