From 049a8d0d709533b6873dd97436beb5d3b16af048 Mon Sep 17 00:00:00 2001
From: "Peter J. Holzer" <hjp@hjp.at>
Date: Mon, 7 Nov 2022 21:06:14 +0100
Subject: [PATCH] Test instant runoff algorithm

---
 utils/instantrunoff | 82 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)
 create mode 100755 utils/instantrunoff

diff --git a/utils/instantrunoff b/utils/instantrunoff
new file mode 100755
index 0000000..a8a5428
--- /dev/null
+++ b/utils/instantrunoff
@@ -0,0 +1,82 @@
+#!/usr/bin/python3
+
+import argparse
+
+import psycopg
+import psycopg.rows
+
+def get_args():
+    ap = argparse.ArgumentParser()
+    ap.add_argument("meet", type=int)
+    ap.add_argument("kind")
+
+    args = ap.parse_args()
+    return args
+
+def get_ballots():
+    db = psycopg.connect(dbname="meeat", user="www-meeat")
+
+    csr = db.cursor(row_factory=psycopg.rows.namedtuple_row)
+
+    kind = args.kind
+    q = f"""
+         select {kind}.*, bod, position
+         from {kind} join {kind}_vote on {kind}.id = {kind}_vote.{kind}
+         where meet = %s
+         order by bod, position
+         """
+    csr.execute(q, (args.meet,))
+
+    last_bod = None
+    ballots = []
+    for r in csr:
+        if r.bod != last_bod:
+            ballot = []
+            ballots.append(ballot)
+            last_bod = r.bod
+        ballot.append(r)
+    return ballots
+
+def dump_ballots(ballots):
+    for ballot in ballots:
+        print ("---")
+        for r in ballot:
+            print(r)
+
+def runoff(ballots):
+    count = {}
+    candidates = {}
+    for ballot in ballots:
+        for r in ballot:
+            count[r.id] = 0
+            candidates[r.id] = r
+    for ballot in ballots:
+        # The votes are sorted by position, so the first entry is the favourite
+        count[ballot[0].id] += 1
+    result = sorted(count.keys(), key=lambda i: count[i])
+    print("result of this round:")
+    for r in result:
+        print(r, count[r])
+    print("striking", result[0])
+    loser = candidates[result[0]]
+    new_ballots = [
+        [
+            r for r in ballot if r.id != loser.id
+        ] for ballot in ballots
+    ]
+    return loser, new_ballots
+
+if __name__ == "__main__":
+    args = get_args()
+
+    ballots = get_ballots()
+
+    result = []
+    while max(len(b) for b in ballots):
+        dump_ballots(ballots)
+        loser, ballots = runoff(ballots)
+        result.append(loser)
+    result = reversed(result)
+    print("final result")
+    for r in result:
+        print(r)