diff --git a/README.md b/README.md index b7cfe0d..4485643 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,8 @@ placing clue numbers, so every puzzle has a solution by construction. - Scroll wheel - and/or a ui button for zoom in/out - [x] Remove red color from rectangle option -- [ ] Allow user to show the "seed" of the puzzle -- [ ] Option to input a custom seed +- [x] Allow user to show the "seed" of the puzzle +- [x] Option to input a custom seed ### Possible Features - Keep local "high score" for each puzzle type diff --git a/shikaku.py b/shikaku.py index 25e0cf9..36d6aec 100644 --- a/shikaku.py +++ b/shikaku.py @@ -527,6 +527,7 @@ class ShikakuGame: self.new_puzzle() def new_puzzle(self, seed=None): + self.current_seed = seed or random.randint(0, 2**31) self.clues, self.solution = generate_puzzle(self.size, seed or random.randint(0, 2**31)) self.player_rects = [] self.start_time = time.time() @@ -678,6 +679,10 @@ class ShikakuWindow(Gtk.ApplicationWindow): clear_btn.connect("clicked", self._on_clear) botbar.append(clear_btn) + seed_btn = Gtk.Button(label="Seed") + seed_btn.add_css_class("action-btn-ghost") + seed_btn.connect("clicked", self._on_seed) + botbar.append(seed_btn) self._status_label = Gtk.Label(label="Draw rectangles around each number — right-click to erase") self._status_label.add_css_class("status-label") @@ -776,6 +781,24 @@ class ShikakuWindow(Gtk.ApplicationWindow): .status-err { color: #c06060; } + .seed-heading { + font-family: "Iosevka", "Monospace"; + font-size: 10px; + letter-spacing: 3px; + color: #6a6070; + } + .seed-value { + font-family: "Iosevka", "Monospace"; + font-size: 20px; + font-weight: 700; + color: #c0b8d8; + letter-spacing: 2px; + } + .seed-entry { + font-family: "Iosevka", "Monospace"; + font-size: 14px; + color: #e8e0ff; + } """ def _update_size_buttons(self): @@ -879,6 +902,84 @@ class ShikakuWindow(Gtk.ApplicationWindow): def _on_zoom_reset(self, btn): self.canvas.set_zoom(1.0) + def _on_seed(self, btn): + dialog = Gtk.Dialog(title="Puzzle Seed", transient_for=self, modal=True) + dialog.set_resizable(False) + + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12) + box.set_margin_top(20) + box.set_margin_bottom(20) + box.set_margin_start(24) + box.set_margin_end(24) + dialog.get_content_area().append(box) + + # Current seed display + current_label = Gtk.Label(label="CURRENT SEED") + current_label.add_css_class("seed-heading") + current_label.set_halign(Gtk.Align.START) + box.append(current_label) + + seed_val = Gtk.Label(label=str(self.game.current_seed)) + seed_val.add_css_class("seed-value") + seed_val.set_selectable(True) + seed_val.set_halign(Gtk.Align.START) + box.append(seed_val) + + sep = Gtk.Separator() + sep.set_margin_top(4) + sep.set_margin_bottom(4) + box.append(sep) + + # Custom seed entry + entry_label = Gtk.Label(label="ENTER SEED") + entry_label.add_css_class("seed-heading") + entry_label.set_halign(Gtk.Align.START) + box.append(entry_label) + + entry = Gtk.Entry() + entry.set_placeholder_text("e.g. 123456789") + entry.set_input_purpose(Gtk.InputPurpose.DIGITS) + entry.add_css_class("seed-entry") + box.append(entry) + + # Buttons + btn_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) + btn_box.set_halign(Gtk.Align.END) + btn_box.set_margin_top(8) + box.append(btn_box) + + cancel_btn = Gtk.Button(label="Cancel") + cancel_btn.add_css_class("action-btn-ghost") + cancel_btn.connect("clicked", lambda b: dialog.close()) + btn_box.append(cancel_btn) + + go_btn = Gtk.Button(label="Start Puzzle") + go_btn.add_css_class("action-btn") + btn_box.append(go_btn) + + def _on_go(b): + text = entry.get_text().strip() + try: + seed = int(text) + except ValueError: + entry.add_css_class("error") + return + dialog.close() + self.game.new_puzzle(seed=seed) + self.canvas._error_rects = set() + self.canvas.zoom = 1.0 + self._zoom_label.set_text("100%") + self._update_canvas_size() + self._status_label.remove_css_class("status-ok") + self._status_label.remove_css_class("status-err") + self._status_label.set_text("Draw rectangles around each number — right-click to erase") + self.canvas.queue_draw() + + go_btn.connect("clicked", _on_go) + entry.connect("activate", _on_go) # Enter key submits + + dialog.present() + # ─── Application entry ────────────────────────────────────────────────────────