Various
authorJakob Cornell <jakob@jcornell.net>
Sat, 22 Jun 2019 05:16:00 +0000 (00:16 -0500)
committerJakob Cornell <jakob@jcornell.net>
Sat, 22 Jun 2019 05:16:00 +0000 (00:16 -0500)
common.py
puzzles/allergic_cliffs/4-2.py [new file with mode: 0644]
puzzles/allergic_cliffs/4.py
puzzles/allergic_cliffs/common.py
rand_troupe.py [new file with mode: 0644]
troupe.py
ui.py

index ce0cf44a91fcdb4515abe3ebabaee4e50b55d2dd..8195c0a47c741dcdebf549367792b474a4302838 100644 (file)
--- a/common.py
+++ b/common.py
@@ -24,7 +24,7 @@ class Zoombini:
                self.attrs = frozenset(attrs)
 
        def __str__(self):
-               return str({type(a).__name__: a.value for a in self.attrs.values()})
+               return str({type(a).__name__: a.value for a in self.attrs})
 
 ALL_ATTRS = frozenset(attr for prop in Zoombini.PROPERTIES for attr in prop)
 
@@ -40,4 +40,4 @@ def save(troupe, path):
 
 def random_zoombini():
        import random
-       return Zoombini({random.choice(list(t)) for t in ALL_ATTRS})
+       return Zoombini({random.choice(list(t)) for t in Zoombini.PROPERTIES})
diff --git a/puzzles/allergic_cliffs/4-2.py b/puzzles/allergic_cliffs/4-2.py
new file mode 100644 (file)
index 0000000..33fdade
--- /dev/null
@@ -0,0 +1,53 @@
+import sys
+
+from zoombinis import common, ui
+from zoombinis.puzzles.allergic_cliffs.common import *
+
+import pathlib
+import random
+
+try:
+       [file_name] = sys.argv[1:]
+       troupe = common.load(pathlib.Path(file_name))
+except:
+       print("args: <troupe-file>", file = sys.stderr)
+       raise
+
+class Instance:
+       def __init__(self, zoombinis, io_agent):
+               self.io_agent = io_agent
+               self.waiting = set(zoombinis)
+               self.across = {cliff: set() for cliff in Cliff}
+
+       def next_pair(self):
+               import itertools
+               def key(pair):
+                       cliff, zoombini = pair
+                       pool = self.across[cliff] if self.across[cliff] else self.waiting - {zoombini}
+                       sim_score = sum(len(zoombini.attrs & z.attrs) for z in pool) / len(pool)
+                       free_ratio = (8 - len(self.across[cliff])) / (8 - len(self.across[cliff.other()]))
+                       return sim_score * free_ratio ** (1/10)
+               return max(itertools.product(Cliff, self.waiting), key = key)
+
+       def send(self, zoombini, cliff):
+               send_fmt = "Send {} across the {} cliff."
+               self.io_agent.print(send_fmt.format(zoombini, cliff.name.lower()))
+               ans = self.io_agent.choose("Did it work?", ["y", "n"])
+               if ans == 'n':
+                       cliff = cliff.other()
+                       self.io_agent.print(send_fmt.format(zoombini, cliff.name.lower()))
+                       self.io_agent.wait()
+               self.across[cliff].add(zoombini)
+               self.waiting.remove(zoombini)
+
+       def run(self):
+               first = max(self.waiting, key = lambda z: sum(len(z.attrs & oz.attrs) for oz in self.waiting) / len(self.waiting))
+               self.send(first, random.choice(list(Cliff)))
+               while not any(len(s) == 8 for s in self.across.values()):
+                       (c, z) = self.next_pair()
+                       self.send(z, c)
+               [full] = [c for c in Cliff if len(self.across[c]) == 8]
+               for z in list(self.waiting):
+                       self.send(z, full.other())
+
+Instance(troupe, ui.Agent(sys.stdin, sys.stderr)).run()
index 80de256b62b0ca1fa998f1658739770b2f25bc46..3da700dcd26422430a066a624aabe74eec1435f3 100644 (file)
@@ -1,7 +1,6 @@
 import sys
 
-import zoombinis.common as common
-import zoombinis.ui as ui
+from zoombinis import common, ui
 from zoombinis.puzzles.allergic_cliffs.common import *
 
 import pathlib
@@ -12,7 +11,7 @@ try:
        troupe = common.load(pathlib.Path(file_name))
 except:
        print("args: <troupe-file>", file = sys.stderr)
-       sys.exit(1)
+       raise
 
 class Instance:
        def __init__(self, zoombinis, io_agent):
@@ -59,7 +58,7 @@ class Instance:
                elif len(self.pos_cand_rules) == 3:
                        self.pos_rules = frozenset(self.pos_cand_rules)
 
-       def send(zoombini, cliff):
+       def send(self, zoombini, cliff):
                send_fmt = "Send {} across the {} cliff."
                self.io_agent.print(send_fmt.format(zoombini, cliff.name.lower()))
                ans = self.io_agent.choose("Did it work?", ["y", "n"])
@@ -67,7 +66,6 @@ class Instance:
                        cliff = cliff.other()
                        self.io_agent.print(send_fmt.format(zoombini, cliff.name.lower()))
                        self.io_agent.wait()
-               self.waiting.remove(zoombini)
                self.across[cliff].add(zoombini)
                if self.pos_cliff is None:
                        for attr in zoombini.attrs:
@@ -79,8 +77,10 @@ class Instance:
        def run(self):
                while self.waiting:
                        z = self.choose_zoombini()
-                       c = self.choose_cliff()
+                       c = self.choose_cliff(z)
                        self.send(z, c)
                        self.waiting.remove(z)
+                       print(self.cand_rules)
+                       print(self.pos_cliff)
 
 Instance(troupe, ui.Agent(sys.stdin, sys.stderr)).run()
index d8f8d757d98854c70f5bd782629e0efab76f785f..4ae751b3f60b9c9088a70ea52fb1b95e1e5535b8 100644 (file)
@@ -1,15 +1,8 @@
-import sys
-sys.path.append('../../..')
 import zoombinis.common as comm
 
 from enum import Enum
 
+Zoombini = comm.Zoombini # for pickle issue workaround
+
 Cliff = Enum('Cliff', ['UPPER', 'LOWER'])
 Cliff.other = lambda self: {Cliff.UPPER: Cliff.LOWER, Cliff.LOWER: Cliff.UPPER}[self]
-
-ALL_RULES = frozenset(
-       (cliff, attr)
-       for cliff in Cliff
-       for prop in comm.Zoombini.Property
-       for attr in prop
-)
diff --git a/rand_troupe.py b/rand_troupe.py
new file mode 100644 (file)
index 0000000..7b8e07f
--- /dev/null
@@ -0,0 +1,6 @@
+import sys
+import pickle
+from zoombinis import common
+
+zs = {common.random_zoombini() for _ in range(16)}
+pickle.dump(zs, sys.stdout.buffer)
index dff8e6b7735f78fe4aea68e67fa5d07476b98bb8..b79b62dabc937661c1a6877da9e2804c0a50c2c7 100644 (file)
--- a/troupe.py
+++ b/troupe.py
@@ -20,15 +20,19 @@ try:
                print(template.format('feet', get_options(Z.Feet)), file = sys.stderr, end = '')
                feet = input()
 
-               zs.append(
-                       Zoombini({
-                               Z.Hair[hair.upper()],
-                               Z.Eyes[eyes.upper()],
-                               Z.Nose[nose.upper()],
-                               Z.Feet[feet.upper()],
-                       })
-               )
-               print("added", file = sys.stderr)
+               try:
+                       zs.append(
+                               Zoombini({
+                                       Z.Hair[hair.upper()],
+                                       Z.Eyes[eyes.upper()],
+                                       Z.Nose[nose.upper()],
+                                       Z.Feet[feet.upper()],
+                               })
+                       )
+               except KeyError:
+                       print("not added!", file = sys.stderr)
+               else:
+                       print("added", file = sys.stderr)
 except EOFError:
        pass
 
diff --git a/ui.py b/ui.py
index 99a54f37ca933b69f9e3fb94e68b34d5f24f70aa..1e6ee59b899573c719a79fabb05227b278a5624a 100644 (file)
--- a/ui.py
+++ b/ui.py
@@ -9,6 +9,7 @@ class Agent:
        def choose(self, message, choices):
                def prompt():
                        self.out_stream.write("{} ({}) ".format(message, '/'.join(choices)))
+                       self.out_stream.flush()
                        return self.in_stream.readline().strip()
                resp = prompt()
                while resp not in choices:
@@ -17,4 +18,5 @@ class Agent:
 
        def wait(self):
                self.out_stream.write("Ready? (Enter) ")
+               self.out_stream.flush()
                self.in_stream.readline()