Skip to content

Commit 860f3f3

Browse files
committed
demo: add wordle game
1 parent e4bd3ea commit 860f3f3

File tree

2 files changed

+114
-2
lines changed

2 files changed

+114
-2
lines changed

demos/index.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
- [Online Gomoku game](./gomoku_game): An online shared Gomoku game (using less than 100 lines of code)
1313
- [Input demo](./input_usage): Demonstrate the usage of PyWebIO input module
1414
- [Output demo](./output_usage): Demonstrate the usage of PyWebIO output module
15-
- [Theme preview](./theme): Demo page with various themes supported by PyWebIO
1615
- [ChatGPT](./chatgpt): A ChatGPT client implemented with PyWebIO
16+
- [Wordle](./wordle): A wordle-like game implemented with PyWebIO
17+
- [Theme preview](./theme): Demo page with various themes supported by PyWebIO
1718
1819
### Data visualization demo
1920
PyWebIO supports for data visualization with the third-party libraries.
@@ -59,8 +60,9 @@
5960
- [在线五子棋游戏](./gomoku_game): 多人协作对战的五子棋游戏 (不到100行代码实现)
6061
- [输入演示](./input_usage): 演示PyWebIO输入模块的用法
6162
- [输出演示](./output_usage): 演示PyWebIO输出模块的用法
62-
- [主题预览](./theme): 展示PyWebIO支持的各种主题
6363
- [ChatGPT](./chatgpt): 使用PyWebIO编写的ChatGPT客户端
64+
- [Wordle](./wordle): 使用PyWebIO编写的猜字游戏(wordle)
65+
- [主题预览](./theme): 展示PyWebIO支持的各种主题
6466
- 更多Demo请见[文档](https://pywebio.readthedocs.io)中示例代码的在线Demo
6567
6668
### 数据可视化demo

demos/wordle.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import time
2+
from pywebio import start_server, config
3+
from pywebio.output import *
4+
from pywebio.session import run_js, local as session_local
5+
6+
TODAY_WORD = 'PYWEBIO' # need to be uppercase
7+
8+
MAX_TRY = 6
9+
WORD_LEN = len(TODAY_WORD)
10+
11+
12+
CSS = """
13+
.pywebio {padding-top: 0} .markdown-body table {display:table; width:250px; margin:10px auto;}
14+
.markdown-body table th, .markdown-body table td {font-weight:bold; padding:0; line-height:50px;}
15+
th>div,td>div {width:50px; height:50px}.btn-light {background-color:#d3d6da;}
16+
@media (max-width: 435px) {.btn{padding:0.375rem 0.5rem;}}
17+
@media (max-width: 355px) {.btn{padding:0.375rem 0.4rem;}}
18+
"""
19+
20+
21+
# To check if a user's input word is actually a legit word
22+
# We just implement a placeholder function in this example
23+
# If a guess word is UNHAPPY, toast a message
24+
def is_word(s):
25+
return 'UNHAPPY' not in s
26+
27+
28+
def on_key_press(char):
29+
if session_local.curr_row >= MAX_TRY or session_local.game_pass:
30+
return
31+
32+
if char == '◀':
33+
session_local.curr_word = session_local.curr_word[:-1]
34+
return clear(f's-{session_local.curr_row}-{len(session_local.curr_word)}')
35+
36+
# show the char in grid
37+
with use_scope(f's-{session_local.curr_row}-{len(session_local.curr_word)}', clear=True):
38+
put_text(char)
39+
40+
session_local.curr_word += char
41+
if len(session_local.curr_word) == WORD_LEN: # submit a word guess
42+
if not is_word(session_local.curr_word):
43+
toast('Not in word list!', color='error')
44+
session_local.curr_word = ''
45+
for i in range(WORD_LEN):
46+
clear(f's-{session_local.curr_row}-{i}')
47+
else:
48+
for idx, c in enumerate(session_local.curr_word):
49+
time.sleep(0.2)
50+
if TODAY_WORD[idx] == c:
51+
session_local.green_chars.add(c)
52+
run_js('$("button:contains(%s)").css({"background-color":"#6aaa64", "color":"white"})' % c)
53+
text_bg = '#6aaa64'
54+
session_local.game_result += '🟩'
55+
elif c in TODAY_WORD:
56+
text_bg = '#c9b458'
57+
session_local.game_result += '🟨'
58+
if c not in session_local.green_chars:
59+
run_js('$("button:contains(%s)").css({"background-color":"#c9b458", "color":"white"})' % c)
60+
else:
61+
text_bg = '#787c7e'
62+
session_local.game_result += '⬜'
63+
run_js('$("button:contains(%s)").css({"background-color":"#787c7e", "color":"white"})' % c)
64+
65+
with use_scope(f's-{session_local.curr_row}-{idx}', clear=True):
66+
put_text(c).style(f'color:white;background:{text_bg}')
67+
68+
session_local.game_result += '\n'
69+
if session_local.curr_word == TODAY_WORD:
70+
toast('Genius', color='success')
71+
session_local.game_pass = True
72+
73+
session_local.curr_row += 1
74+
session_local.curr_word = ''
75+
76+
if session_local.game_pass:
77+
message = f'Wordle {session_local.curr_row}/{MAX_TRY}\n' + session_local.game_result
78+
with popup("Game Result", size='small'):
79+
put_text(message).style('text-align: center')
80+
put_button('Share', color='success', onclick=lambda: toast('Copied to clipboard') or run_js("""navigator.clipboard.write([new ClipboardItem({"text/plain":new Blob([text],{type:"text/plain"})})]);""", text=message)).style('text-align: center')
81+
82+
83+
@config(title="WORDLE with PyWebIO", description="A wordle-like game implemented with PyWebIO", css_style=CSS)
84+
def main():
85+
put_markdown(
86+
'# WORDLE \n A pure python implementation of a [Wordle-like game](https://en.wikipedia.org/wiki/Wordle), using PyWebIO library. '
87+
'[Source code](https://github.com/pywebio/PyWebIO/blob/dev/demos/wordle.py)'
88+
).style('text-align:center')
89+
90+
grid = [
91+
[put_scope(f's-{x}-{y}', content=put_text(' ')) for y in range(WORD_LEN)]
92+
for x in range(MAX_TRY)
93+
]
94+
put_table(grid).style('text-align: center')
95+
96+
keyboard = [
97+
put_buttons([dict(label=c, value=c, color='light') for c in keys], on_key_press, serial_mode=True)
98+
for keys in ['QWERTYUIOP', 'ASDFGHJKL', 'ZXCVBNM◀']
99+
]
100+
put_column(keyboard).style('text-align: center')
101+
102+
session_local.curr_row = 0
103+
session_local.curr_word = ''
104+
session_local.green_chars = set()
105+
session_local.game_pass = False
106+
session_local.game_result = ''
107+
108+
109+
if __name__ == '__main__':
110+
start_server(main, port=8080, cdn=False)

0 commit comments

Comments
 (0)