Skip to content

Commit 18d2f72

Browse files
committed
Add start menu
1 parent 178f7b1 commit 18d2f72

File tree

3 files changed

+131
-13
lines changed

3 files changed

+131
-13
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Due to implementing the `nCurses` interface, there has been some feature regress
5151
- Toggle _kifu_ (game record) with `n` during gameplay
5252
- Undo on user v. user games
5353
- Only allows legal moves
54+
- Start menu with settings
5455

5556

5657
### Future

TODO.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
## Features
44

55
### New
6-
- Start menu
7-
- Accept faster double click
8-
- Center (offset) board by terminal size
96
- SGF import/export
107
- Multiple game branch (undo)
11-
- A, B, triangle symbols
128
- Allow user specified GTP engine
139
- Scoring (external engine?)
10+
- Handle screen resize gracefully
11+
12+
### Improvements
1413
- Screensaver w/ random stones, 1sec update
14+
- Accept faster double click
15+
- Center (offset) board by terminal size
16+
- A, B, triangle symbols
1517

1618
## Bugs
1719
- Fix GTP engine game lag/glitch first turn

dango.py

Lines changed: 124 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@
88
import curses
99
from curses import wrapper
1010

11+
KOMI = 6.5
12+
SIZE = 19
13+
1114
BLACK = -1
1215
EMPTY = 0
1316
WHITE = 1
14-
SIZE = 19
1517
MARGIN_X = 3
1618
MARGIN_Y = 1
1719
MESSAGE = (SIZE + 2, 2)
1820
EMPTY = '·'
1921
STONE = '●'
2022
SPACE = ' '
21-
2223
VERSION = "0.4.2"
2324

2425
"""note that implementing curses will have breaking changes on API
@@ -98,7 +99,6 @@ def logo():
9899

99100

100101
def curses_setup(stdscr):
101-
"""Sets up the screen and paints the pond blue"""
102102
stdscr.clear() # Clear the screen
103103
curses.noecho() # Turn off echoing of keys
104104
curses.cbreak() # Turn off normal tty line buffering
@@ -339,7 +339,6 @@ def clear_message(stdscr):
339339

340340

341341
def play(stdscr):
342-
curses_setup(stdscr)
343342
moves = [] # moves (y,x), for undo. None if pass
344343
captures = [] # list of stones removed, per move
345344
n_toggle = 0
@@ -357,7 +356,6 @@ def play(stdscr):
357356
if c == 10 or c == ord(" "):
358357
if stones.legal_placement(y, x, color, captures):
359358
place_piece(y, x, color, stones, moves, captures, stdscr)
360-
# moves.append((y, x))
361359
color *= -1
362360
update_to_play = True
363361
else:
@@ -376,9 +374,7 @@ def play(stdscr):
376374
if stones.legal_placement(y, x, color, captures):
377375
place_piece(y, x, color, stones,
378376
moves, captures, stdscr)
379-
# moves.append((y, x))
380377
color *= -1
381-
update_to_play = True
382378
else:
383379
error_out(stdscr, "Not a valid move")
384380
break
@@ -412,7 +408,6 @@ def play(stdscr):
412408
n_toggle = 0
413409
else:
414410
i = 1
415-
print(moves)
416411
for move in moves:
417412
if move in stones.black:
418413
stdscr.addstr(move[0], move[1], str(i),
@@ -469,10 +464,130 @@ def play(stdscr):
469464
stdscr.refresh()
470465

471466

467+
def menu(stdscr):
468+
global SIZE
469+
global KOMI
470+
global MESSAGE
471+
curses.curs_set(0) # Hide the cursor
472+
stdscr.clear()
473+
474+
# Define menu items and their associated values
475+
menu_items = [
476+
"Start",
477+
"Size",
478+
"Komi",
479+
"Players",
480+
"Help",
481+
"Exit"
482+
]
483+
484+
screen_height, screen_width = stdscr.getmaxyx()
485+
center_y = screen_height // 2
486+
center_x = screen_width // 2
487+
square_height = 9
488+
square_width = square_height * 2
489+
square_y = center_y - (square_height // 2)
490+
square_x = center_x - (square_width // 2)
491+
492+
for y in range(square_y, square_y + square_height):
493+
for x in range(square_x, square_x + square_width, 2):
494+
stdscr.addstr(y, x, EMPTY + SPACE, curses.color_pair(1))
495+
stdscr.addstr(square_y + 1, square_x + 10, "dango", curses.color_pair(1) | curses.A_BOLD)
496+
stdscr.addstr(square_y + 2, square_x + 10, STONE, curses.color_pair(7))
497+
stdscr.addstr(square_y + 3, square_x + 12, STONE, curses.color_pair(2))
498+
stdscr.addstr(square_y + 4, square_x + 14, STONE, curses.color_pair(8))
499+
500+
menu_y = center_y - (len(menu_items) // 2)
501+
for idx, item in enumerate(menu_items):
502+
x = center_x + (square_width // 2) + 4
503+
y = menu_y + idx
504+
stdscr.addstr(y, x, f" {item}")
505+
506+
menu_len = len(menu_items)
507+
max_len = len(max(menu_items, key=len))
508+
selected_item = 0
509+
selected_value = None
510+
tally = 0
511+
512+
while True:
513+
if tally == 3: # activate usage threshold
514+
stdscr.addstr(square_y + 10, square_x, "Use up-down to select item,", curses.color_pair(0))
515+
stdscr.addstr(square_y + 11, square_x, "left-right to change value, ", curses.color_pair(0))
516+
stdscr.addstr(square_y + 12, square_x, "and space or enter to continue.", curses.color_pair(0))
517+
tally = 0
518+
519+
stdscr.addstr(center_y + (len(menu_items) // 2) + 2, 0, " " * screen_width)
520+
menu_values = [None, SIZE, KOMI, None, None, None] # refresh SIZE, KOMI
521+
522+
for idx, item in enumerate(menu_items):
523+
x = center_x + (square_width // 2) + 4
524+
y = menu_y + idx
525+
if idx == selected_item:
526+
if selected_item == 1 or selected_item == 2: # SIZE or KOMI
527+
stdscr.addstr(y, x, " " * 5)
528+
stdscr.addstr(y, x, f" {menu_values[selected_item]}", curses.A_BLINK | curses.A_BOLD)
529+
stdscr.addstr(y, x - 2, ">")
530+
stdscr.addstr(y, x + len(str(menu_values[selected_item])) + 3, "<")
531+
else:
532+
# stdscr.addstr(y, x, f" {item}")
533+
stdscr.addstr(y, x - 2, ">")
534+
stdscr.addstr(y, x, f" {item}", curses.color_pair(0) | curses.A_BOLD)
535+
stdscr.addstr(y, x + len(item) + 3, "<")
536+
else:
537+
stdscr.addstr(y, x, f" {item} ")
538+
stdscr.addstr(y, x - 2, " ")
539+
stdscr.addstr(y, x + len(item) + 3, " ")
540+
541+
stdscr.refresh()
542+
c = stdscr.getch()
543+
if c == curses.KEY_UP:
544+
selected_item = (selected_item - 1) % menu_len
545+
selected_value = menu_values[selected_item]
546+
elif c == curses.KEY_DOWN:
547+
selected_item = (selected_item + 1) % menu_len
548+
selected_value = menu_values[selected_item]
549+
elif c == curses.KEY_LEFT:
550+
if selected_item == 1:
551+
SIZE = max(1, SIZE - 1)
552+
if selected_item == 2:
553+
KOMI = max(0, KOMI - 1)
554+
elif c == curses.KEY_RIGHT:
555+
if selected_item == 1:
556+
SIZE += 1
557+
if selected_item == 2:
558+
KOMI += 0.5
559+
elif c == 10 or c == ord(" "):
560+
for y in range(square_y + 10, square_y + 13): # clear help/usage
561+
stdscr.move(y, square_x)
562+
stdscr.clrtoeol()
563+
if selected_item == 0:
564+
MESSAGE = (SIZE + 2, 2)
565+
break
566+
elif selected_item == menu_len -1 :
567+
exit()
568+
elif selected_item == 3:
569+
stdscr.addstr(square_y + 10, square_x, "Only two-player is currently enabled.", curses.color_pair(0))
570+
elif selected_item == 4:
571+
stdscr.addstr(square_y + 10, square_x, "Run 'dango -h' for help.", curses.color_pair(0))
572+
elif c == ord('q'):
573+
exit()
574+
else:
575+
tally += 1
576+
577+
578+
def main(stdscr):
579+
curses_setup(stdscr)
580+
menu(stdscr)
581+
stdscr.clear()
582+
play(stdscr)
583+
584+
472585
if __name__ == "__main__":
473586
try:
474-
wrapper(play)
587+
wrapper(main)
475588
except KeyboardInterrupt:
476589
print('Thank you for the game.')
590+
except curses.error:
591+
print("Terminal not large enough, resize and try again.")
477592
# except:
478593
# print("Something went wrong. Let us know, we'll try to fix it!")

0 commit comments

Comments
 (0)