From 709ef721afb0dc239e5246f5205f701b99d4a199 Mon Sep 17 00:00:00 2001
From: Runxi Yu <me@runxiyu.org>
Date: Tue, 25 Mar 2025 03:25:30 +0800
Subject: Add Python challenge-solver script

---
 challenge.html   |  2 +-
 static/solver.py | 29 +++++++++++++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)
 create mode 100644 static/solver.py

diff --git a/challenge.html b/challenge.html
index 77e1eed..03c5988 100644
--- a/challenge.html
+++ b/challenge.html
@@ -48,7 +48,7 @@
 		</section>
 
 		<section>
-			<p><a href="/.powxy/static/solver.c">A C implementation of the challenge solver</a> is available.</p>
+			<p><a href="/.powxy/static/solver.c">A C implementation</a> and <a href="/.powxy/static/solver.py">a Python implementation</a> of the challenge solver are available.</p>
 		</section>
 	</main>
 </body>
diff --git a/static/solver.py b/static/solver.py
new file mode 100644
index 0000000..0fe66f0
--- /dev/null
+++ b/static/solver.py
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: BSD-2-Clause
+# SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
+
+import base64
+import hashlib
+import sys
+import struct
+
+def validate_bit_zeros(bs: bytes, n: int) -> bool:
+	q, r = divmod(n, 8)
+	if any(b != 0 for b in bs[:q]):
+		return False
+	if r and (bs[q] & (0xFF << (8 - r))):
+		return False
+	return True
+
+decoded = base64.b64decode(sys.argv[1])
+difficulty = int(sys.argv[2])
+next_val = 0
+
+while True:
+	h = hashlib.sha256(decoded + struct.pack("Q", next_val)).digest()
+	if validate_bit_zeros(h, difficulty):
+		break
+	next_val = (next_val + 1) & 0xFFFFFFFFFFFFFFFF
+	if next_val == 0:
+        raise ValueError("overflow")
+
+print(base64.b64encode(struct.pack("Q", next_val)).decode())
-- 
cgit v1.2.3