aboutsummaryrefslogtreecommitdiff
path: root/challenge-286/ppentchev/python/scripts/ch-2.py
diff options
context:
space:
mode:
Diffstat (limited to 'challenge-286/ppentchev/python/scripts/ch-2.py')
-rwxr-xr-xchallenge-286/ppentchev/python/scripts/ch-2.py109
1 files changed, 109 insertions, 0 deletions
diff --git a/challenge-286/ppentchev/python/scripts/ch-2.py b/challenge-286/ppentchev/python/scripts/ch-2.py
new file mode 100755
index 0000000000..b8c0819589
--- /dev/null
+++ b/challenge-286/ppentchev/python/scripts/ch-2.py
@@ -0,0 +1,109 @@
+#!/usr/bin/python3
+# SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
+# SPDX-License-Identifier: BSD-2-Clause
+"""Pick a random word from this script's source file and output it."""
+
+from __future__ import annotations
+
+import enum
+import os
+import sys
+import typing
+
+
+if typing.TYPE_CHECKING:
+ from collections.abc import Callable, Iterable, Iterator
+ from typing import Final, Self
+
+
+TEST_SEQUENCES: Final = [
+ [2, 1, 4, 5, 6, 3, 0, 2],
+ [0, 5, 3, 2],
+ [9, 2, 1, 4, 5, 6, 0, 7, 3, 1, 3, 5, 7, 9, 0, 8],
+]
+"""The sample sequences from the problem."""
+
+
+class OrderIterState(enum.IntEnum):
+ """The states that an `OrderIter` object may be in."""
+
+ STASH = 0
+ """Stash a value to pass to `min()` or `max()` later."""
+
+ YIELD = 1
+ """Perform a `min()` or `max()` operation, yield the result."""
+
+
+class OrderIter:
+ """Iterate over the numbers of a single round."""
+
+ ints: Iterator[int]
+ """The list of integers to process."""
+
+ state: OrderIterState
+ """The current state of the iterator."""
+
+ stashed: int
+ """The last stashed number."""
+
+ op: Callable[[int, int], int]
+ """The operation to perform when yielding a value."""
+
+ def __init__(self, ints: Iterable[int]) -> None:
+ """Construct an `OrderIter` object with the specified numbers to process."""
+ self.ints = iter(ints)
+ self.state = OrderIterState.STASH
+ self.stashed = -616
+ self.op = min
+
+ def __iter__(self) -> Self:
+ """Return the `OrderIter` object itself as an iterator."""
+ return self
+
+ def __next__(self) -> int:
+ """Calculate and return the next number."""
+ match self.state:
+ case OrderIterState.STASH:
+ self.stashed = next(self.ints)
+ self.state = OrderIterState.YIELD
+ return next(self)
+
+ case OrderIterState.YIELD:
+ res: Final = self.op(self.stashed, next(self.ints))
+ self.state = OrderIterState.STASH
+ self.op = max if self.op == min else min
+ return res
+
+
+def order_iter(ints: list[int]) -> list[int]:
+ """Run a single round using an `OrderIter` object."""
+ return list(OrderIter(ints))
+
+
+def run_order_game(ints: list[int]) -> int:
+ """Run the order game until there is only a single integer left."""
+ if not ints:
+ raise RuntimeError(repr(ints))
+
+ while len(ints) > 1:
+ ints = order_iter(ints)
+
+ return ints[0]
+
+
+def parse_stdin() -> list[int]:
+ """Read a line from the standard input, parse it as a list of integers."""
+ return [int(word) for word in sys.stdin.readline().split() if word]
+
+
+def main() -> None:
+ """Find the source file, select a word, output it."""
+ if os.environ.get("PWC_FROM_STDIN", "") == "1":
+ print(run_order_game(parse_stdin())) # noqa: T201 # this is the whole point
+ else:
+ for ints in TEST_SEQUENCES:
+ print(run_order_game(ints)) # noqa: T201 # this is the whole point
+
+
+if __name__ == "__main__":
+ main()