Skip to content

Commit 031302d

Browse files
committed
Add tic-tac-toe game to pytips
1 parent e4d8354 commit 031302d

File tree

13 files changed

+402
-0
lines changed

13 files changed

+402
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import PySimpleGUI as sg
2+
3+
4+
def main():
5+
window = sg.Window("Tic-Tac-Toe", layout=[[]], margins=(100, 50))
6+
7+
while True:
8+
event, values = window.read()
9+
if event == "Exit" or event == sg.WIN_CLOSED:
10+
break
11+
12+
window.close()
13+
14+
15+
if __name__ == "__main__":
16+
main()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import PySimpleGUI as sg
2+
3+
BLANK_IMAGE_PATH = "assets/BLANK.png"
4+
5+
6+
def main():
7+
layout = [
8+
[
9+
sg.Button(
10+
size=(10, 6),
11+
key=(row, col),
12+
button_color=("white", "white"),
13+
image_filename=BLANK_IMAGE_PATH,
14+
)
15+
for row in range(3)
16+
]
17+
for col in range(3)
18+
]
19+
window = sg.Window("Tic-Tac-Toe", layout)
20+
21+
while True:
22+
event, values = window.read()
23+
if event == "Exit" or event == sg.WIN_CLOSED:
24+
break
25+
26+
window.close()
27+
28+
29+
if __name__ == "__main__":
30+
main()
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import io
2+
import PySimpleGUI as sg
3+
4+
from PIL import Image
5+
6+
7+
PLAYER_X_IMAGE_PATH = "assets/X.png"
8+
PLAYER_O_IMAGE_PATH = "assets/O.png"
9+
BLANK_IMAGE_PATH = "assets/BLANK.png"
10+
11+
12+
def update_game(button, player):
13+
"""
14+
Update the game
15+
"""
16+
original_player = player
17+
if player == "X":
18+
filename = PLAYER_X_IMAGE_PATH
19+
player = "O"
20+
else:
21+
filename = PLAYER_O_IMAGE_PATH
22+
player = "X"
23+
24+
bio = io.BytesIO()
25+
image = Image.open(filename)
26+
image.save(bio, format="PNG")
27+
28+
if not button.metadata:
29+
button.update(text=player, image_data=bio.getvalue())
30+
button.metadata = original_player
31+
return player
32+
33+
return original_player
34+
35+
36+
def main():
37+
layout = [
38+
[
39+
sg.Button(
40+
size=(10, 6),
41+
key=(row, col),
42+
button_color=("white", "white"),
43+
image_filename=BLANK_IMAGE_PATH,
44+
)
45+
for row in range(3)
46+
]
47+
for col in range(3)
48+
]
49+
window = sg.Window("Tic-Tac-Toe", layout)
50+
51+
player = "X"
52+
while True:
53+
event, values = window.read()
54+
if event == "Exit" or event == sg.WIN_CLOSED:
55+
break
56+
if isinstance(event, tuple):
57+
btn_clicked = window[event]
58+
player = update_game(btn_clicked, player)
59+
60+
window.close()
61+
62+
63+
if __name__ == "__main__":
64+
main()
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import io
2+
3+
import PySimpleGUI as sg
4+
from PIL import Image
5+
6+
PLAYER_X_IMAGE_PATH = "assets/X.png"
7+
PLAYER_O_IMAGE_PATH = "assets/O.png"
8+
BLANK_IMAGE_PATH = "assets/BLANK.png"
9+
INITIAL_PLAYER = "X"
10+
11+
12+
def ask_if_play_again(player):
13+
"""
14+
Ask the user if they want to play again or quit
15+
"""
16+
if player is None:
17+
message = "Tied Game!"
18+
else:
19+
message = f"{player} won!"
20+
layout = [
21+
[sg.Text(f"{message} Do you want to play again or Quit?")],
22+
[sg.Button("Restart"), sg.Button("Quit")],
23+
]
24+
event, values = sg.Window("Play Again?", layout, modal=True).read(
25+
close=True
26+
)
27+
return True if event == "Restart" else False
28+
29+
30+
def check_if_won(winning_configurations):
31+
"""
32+
Check if anyone has won yet
33+
"""
34+
winner = None
35+
for configuration in winning_configurations:
36+
game_pieces = {btn.metadata for btn in configuration}
37+
is_won = None not in game_pieces and len(game_pieces) == 1
38+
if is_won:
39+
winner = game_pieces.pop()
40+
mark_win([*configuration])
41+
return (True, winner)
42+
43+
# Check if tied game
44+
data = [
45+
btn.metadata
46+
for configuration in winning_configurations
47+
for btn in configuration
48+
]
49+
50+
if None not in data:
51+
# Tied game
52+
return (None, winner)
53+
54+
# Keep playing
55+
return (False, winner)
56+
57+
58+
def get_winning_configurations(buttons):
59+
"""
60+
Returns a list of methods to win the game
61+
"""
62+
horizontal_ways_to_win = [
63+
[buttons[0][0], buttons[1][0], buttons[2][0]],
64+
[buttons[0][1], buttons[1][1], buttons[2][1]],
65+
[buttons[0][2], buttons[1][2], buttons[2][2]],
66+
]
67+
vertical_ways_to_win = [
68+
[buttons[0][0], buttons[0][1], buttons[0][2]],
69+
[buttons[1][0], buttons[1][1], buttons[1][2]],
70+
[buttons[2][0], buttons[2][1], buttons[2][2]],
71+
]
72+
diagonal_ways_to_win = [
73+
[buttons[0][0], buttons[1][1], buttons[2][2]],
74+
[buttons[0][2], buttons[1][1], buttons[2][0]],
75+
]
76+
return horizontal_ways_to_win + vertical_ways_to_win + diagonal_ways_to_win
77+
78+
79+
def mark_win(buttons):
80+
"""
81+
Mark the winning buttons with a different background color
82+
"""
83+
for button in buttons:
84+
button.update(button_color=["green", "green"])
85+
86+
87+
def reset_game(buttons):
88+
"""
89+
Reset the game to play again
90+
"""
91+
bio = io.BytesIO()
92+
image = Image.open(BLANK_IMAGE_PATH)
93+
image.save(bio, format="PNG")
94+
for row in buttons:
95+
for button in row:
96+
button.update(
97+
image_data=bio.getvalue(), button_color=["white", "white"]
98+
)
99+
button.metadata = None
100+
101+
102+
def update_game(button, player):
103+
"""
104+
Update the game
105+
"""
106+
original_player = player
107+
if player == "X":
108+
filename = PLAYER_X_IMAGE_PATH
109+
player = "O"
110+
else:
111+
filename = PLAYER_O_IMAGE_PATH
112+
player = "X"
113+
114+
bio = io.BytesIO()
115+
image = Image.open(filename)
116+
image.save(bio, format="PNG")
117+
118+
if not button.metadata:
119+
button.update(text=player, image_data=bio.getvalue())
120+
button.metadata = original_player
121+
return player
122+
123+
return original_player
124+
125+
126+
def main():
127+
"""
128+
Create GUI and manage UI events
129+
"""
130+
layout = [
131+
[
132+
sg.Button(
133+
size=(7, 5),
134+
button_text=f"({row} {col})",
135+
key=(row, col),
136+
button_color=("white", "white"),
137+
image_filename=BLANK_IMAGE_PATH,
138+
)
139+
for row in range(3)
140+
]
141+
for col in range(3)
142+
]
143+
window = sg.Window("Tic-Tac-Toe", layout)
144+
ways_to_win = get_winning_configurations(layout)
145+
146+
player = INITIAL_PLAYER
147+
while True:
148+
event, values = window.read()
149+
if event == "Exit" or event == sg.WIN_CLOSED:
150+
break
151+
if isinstance(event, tuple):
152+
btn_clicked = window[event]
153+
player = update_game(btn_clicked, player)
154+
winning_configuration, winner = check_if_won(ways_to_win)
155+
if winning_configuration is not False:
156+
should_restart = ask_if_play_again(winner)
157+
if should_restart is False:
158+
# Close the application
159+
break
160+
player = INITIAL_PLAYER
161+
reset_game(layout)
162+
163+
window.close()
164+
165+
166+
if __name__ == "__main__":
167+
main()
1.62 KB
Loading
9.07 KB
Loading
8.17 KB
Loading

getattr_hack.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Works in Python 3.7+
2+
3+
def __getattr__(expression):
4+
operator, number = expression.split("_")
5+
number = int(number)
6+
operations = {"times": lambda val: val * number}
7+
if operator not in operations:
8+
print(f"Unknown operator: {operator}")
9+
return print
10+
return operations[operator]

graphs/sinewave.html

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
2+
3+
4+
5+
<!DOCTYPE html>
6+
<html lang="en">
7+
8+
<head>
9+
10+
<meta charset="utf-8">
11+
<title>Bokeh Plot</title>
12+
13+
14+
15+
16+
17+
18+
19+
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.3.3.min.js" integrity="sha384-dM3QQsP+wXdHg42wTqW85BjZQdLNNIXqlPw/BgKoExPmTG7ZLML4EGqLMfqHT6ON" crossorigin="anonymous"></script>
20+
<script type="text/javascript">
21+
Bokeh.set_log_level("info");
22+
</script>
23+
24+
25+
26+
27+
</head>
28+
29+
30+
<body>
31+
32+
33+
34+
35+
36+
37+
<div class="bk-root" id="70f51f80-24a3-4b39-b160-1153dcd983d3" data-root-id="1051"></div>
38+
39+
40+
41+
42+
43+
<script type="application/json" id="1152">
44+
{"8a950a33-dc35-452e-a385-f7cdc5058c5b":{"defs":[],"roots":{"references":[{"attributes":{"active_multi":null,"tools":[{"id":"1021"},{"id":"1022"},{"id":"1023"},{"id":"1024"},{"id":"1025"},{"id":"1026"}]},"id":"1028","type":"Toolbar"},{"attributes":{},"id":"1018","type":"BasicTicker"},{"attributes":{},"id":"1011","type":"LinearScale"},{"attributes":{"children":[[{"id":"1002"},0,0]]},"id":"1051","type":"GridBox"},{"attributes":{"below":[{"id":"1013"}],"center":[{"id":"1016"},{"id":"1020"}],"height":500,"left":[{"id":"1017"}],"renderers":[{"id":"1038"}],"title":{"id":"1003"},"toolbar":{"id":"1028"},"toolbar_location":null,"width":500,"x_range":{"id":"1005"},"x_scale":{"id":"1009"},"y_range":{"id":"1007"},"y_scale":{"id":"1011"}},"id":"1002","subtype":"Figure","type":"Plot"},{"attributes":{},"id":"1007","type":"DataRange1d"},{"attributes":{},"id":"1005","type":"DataRange1d"},{"attributes":{"formatter":{"id":"1045"},"major_label_policy":{"id":"1044"},"ticker":{"id":"1014"}},"id":"1013","type":"LinearAxis"},{"attributes":{},"id":"1048","type":"UnionRenderers"},{"attributes":{"text":"Sine"},"id":"1003","type":"Title"},{"attributes":{"fill_alpha":{"value":0.1},"fill_color":{"value":"navy"},"line_alpha":{"value":0.1},"line_color":{"value":"navy"},"size":{"value":10},"x":{"field":"x"},"y":{"field":"y"}},"id":"1037","type":"Circle"},{"attributes":{},"id":"1009","type":"LinearScale"},{"attributes":{},"id":"1049","type":"Selection"},{"attributes":{},"id":"1014","type":"BasicTicker"},{"attributes":{"source":{"id":"1035"}},"id":"1039","type":"CDSView"},{"attributes":{},"id":"1025","type":"ResetTool"},{"attributes":{},"id":"1024","type":"SaveTool"},{"attributes":{"data":{"x":{"__ndarray__":"AAAAAAAAAACRGkt8Vz/AP5EaS3xXP9A/2qdwOgNf2D+RGkt8Vz/gPzXhXVstT+Q/2qdwOgNf6D9+boMZ2W7sP5EaS3xXP/A/433Ua0JH8j814V1bLU/0P4dE50oYV/Y/2qdwOgNf+D8sC/op7mb6P35ugxnZbvw/0NEMCcR2/j+RGkt8Vz8AQDrMD/RMQwFA433Ua0JHAkCML5njN0sDQDXhXVstTwRA3pIi0yJTBUCHROdKGFcGQDD2q8INWwdA2qdwOgNfCECDWTWy+GIJQCwL+inuZgpA1by+oeNqC0B+boMZ2W4MQCcgSJHOcg1A0NEMCcR2DkB5g9GAuXoPQJEaS3xXPxBAZnMtOFLBEEA6zA/0TEMRQA8l8q9HxRFA433Ua0JHEkC41rYnPckSQIwvmeM3SxNAYYh7nzLNE0A14V1bLU8UQAo6QBco0RRA3pIi0yJTFUCz6wSPHdUVQIdE50oYVxZAXJ3JBhPZFkAw9qvCDVsXQAVPjn4I3RdA2qdwOgNfGECuAFP2/eAYQINZNbL4YhlAV7IXbvPkGUAsC/op7mYaQABk3OXo6BpA1by+oeNqG0CpFaFd3uwbQH5ugxnZbhxAUsdl1dPwHEAnIEiRznIdQPt4Kk3J9B1A0NEMCcR2HkCkKu/EvvgeQHmD0YC5eh9ATdyzPLT8H0CRGkt8Vz8gQPtGPNpUgCBAZnMtOFLBIEDQnx6WTwIhQDrMD/RMQyFApPgAUkqEIUAPJfKvR8UhQHlR4w1FBiJA433Ua0JHIkBNqsXJP4giQLjWtic9ySJAIgOohToKI0CML5njN0sjQPZbikE1jCNAYYh7nzLNI0DLtGz9Lw4kQDXhXVstTyRAoA1PuSqQJEAKOkAXKNEkQHRmMXUlEiVA3pIi0yJTJUBJvxMxIJQlQLPrBI8d1SVAHRj27BoWJkCHROdKGFcmQPJw2KgVmCZAXJ3JBhPZJkDGybpkEBonQDD2q8INWydAmyKdIAucJ0AFT45+CN0nQG97f9wFHihA2qdwOgNfKEBE1GGYAKAoQK4AU/b94ChAGC1EVPshKUA=","dtype":"float64","order":"little","shape":[100]},"y":{"__ndarray__":"AAAAAAAAAADwgwJ4LjTAP5qI2QHPEtA/+5MNU1HJ1z8ZTT7q2B3fP0aFmgwa+eI/ABg/lCAV5j+IQCEcMdboP3fOhmLzMOs/EyzujrQb7T/9Izwlj47uPxccgYyLg+8/g6xao7j27z9mZrL8O+bvP7+X94FZUu8/xVNiW3I97j9RB8Ai+6vsP5FWKYlppOo/oig6uxkv6D/Sh2bxK1blPyZGRbpaJeI/8qg+VZVT3T/4C4p0qOPVP+UppLIiM8w/3FNHxZdVuD/I23zJpD6gv+3ENsZjOcS/wo12/ecH0r/egWKM2KjZv2MTsJELcOC/83ixWvXH47+258MBZM7mv5eCD4nhdum/rExY6Hq267+TIxAn7YPtvwSssILL1+6/70I4BJ6s77/vOc8G+P7vvzTGy1KGze+/3176khQZ77/C8a4NiuTtvzGcHq/dNOy/mEFFlgER6r+EGZN4xoHnvx8WPFK3keS/Cli7+O1M4b+HQNqEwoHbv9kfIiFb+NO/gKyggGI5yL8EhrXMjDywvyCGtcyMPLA/jqyggGI5yD/gHyIhW/jTP4BA2oTCgds/Dli7+O1M4T8cFjxSt5HkP4cZk3jGgec/lEFFlgER6j8ynB6v3TTsP8Hxrg2K5O0/4F76khQZ7z80xstShs3vP+85zwb4/u8/8EI4BJ6s7z8DrLCCy9fuP5UjECftg+0/p0xY6Hq26z+Vgg+J4XbpP7fnwwFkzuY/9nixWvXH4z9cE7CRC3DgP9eBYozYqNk/w412/ecH0j/+xDbGYznEP0/bfMmkPqA/+VNHxZdVuL/UKaSyIjPMv+gLinSo49W/+Kg+VZVT3b8mRkW6WiXiv8+HZvErVuW/pig6uxkv6L+TVimJaaTqv1EHwCL7q+y/xFNiW3I97r/Al/eBWVLvv2Zmsvw75u+/g6xao7j2778YHIGMi4Pvv/sjPCWPju6/EyzujrQb7b94zoZi8zDrv4xAIRwx1ui//Bc/lCAV5r9GhZoMGvnivx5NPurYHd+/65MNU1HJ17+SiNkBzxLQv/GDAnguNMC/B1wUMyamwbw=","dtype":"float64","order":"little","shape":[100]}},"selected":{"id":"1049"},"selection_policy":{"id":"1048"}},"id":"1035","type":"ColumnDataSource"},{"attributes":{"fill_alpha":{"value":0.5},"fill_color":{"value":"navy"},"line_alpha":{"value":0.5},"line_color":{"value":"navy"},"size":{"value":10},"x":{"field":"x"},"y":{"field":"y"}},"id":"1036","type":"Circle"},{"attributes":{},"id":"1045","type":"BasicTickFormatter"},{"attributes":{},"id":"1042","type":"BasicTickFormatter"},{"attributes":{},"id":"1026","type":"HelpTool"},{"attributes":{},"id":"1021","type":"PanTool"},{"attributes":{"axis":{"id":"1013"},"ticker":null},"id":"1016","type":"Grid"},{"attributes":{"axis":{"id":"1017"},"dimension":1,"ticker":null},"id":"1020","type":"Grid"},{"attributes":{},"id":"1041","type":"AllLabels"},{"attributes":{"overlay":{"id":"1027"}},"id":"1023","type":"BoxZoomTool"},{"attributes":{},"id":"1022","type":"WheelZoomTool"},{"attributes":{},"id":"1044","type":"AllLabels"},{"attributes":{"formatter":{"id":"1042"},"major_label_policy":{"id":"1041"},"ticker":{"id":"1018"}},"id":"1017","type":"LinearAxis"},{"attributes":{"data_source":{"id":"1035"},"glyph":{"id":"1036"},"hover_glyph":null,"muted_glyph":null,"nonselection_glyph":{"id":"1037"},"view":{"id":"1039"}},"id":"1038","type":"GlyphRenderer"},{"attributes":{"bottom_units":"screen","fill_alpha":0.5,"fill_color":"lightgrey","left_units":"screen","level":"overlay","line_alpha":1.0,"line_color":"black","line_dash":[4,4],"line_width":2,"right_units":"screen","syncable":false,"top_units":"screen"},"id":"1027","type":"BoxAnnotation"}],"root_ids":["1051"]},"title":"Bokeh Application","version":"2.3.3"}}
45+
</script>
46+
<script type="text/javascript">
47+
(function() {
48+
var fn = function() {
49+
Bokeh.safely(function() {
50+
(function(root) {
51+
function embed_document(root) {
52+
53+
var docs_json = document.getElementById('1152').textContent;
54+
var render_items = [{"docid":"8a950a33-dc35-452e-a385-f7cdc5058c5b","root_ids":["1051"],"roots":{"1051":"70f51f80-24a3-4b39-b160-1153dcd983d3"}}];
55+
root.Bokeh.embed.embed_items(docs_json, render_items);
56+
57+
}
58+
if (root.Bokeh !== undefined) {
59+
embed_document(root);
60+
} else {
61+
var attempts = 0;
62+
var timer = setInterval(function(root) {
63+
if (root.Bokeh !== undefined) {
64+
clearInterval(timer);
65+
embed_document(root);
66+
} else {
67+
attempts++;
68+
if (attempts > 100) {
69+
clearInterval(timer);
70+
console.log("Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing");
71+
}
72+
}
73+
}, 10, root)
74+
}
75+
})(window);
76+
});
77+
};
78+
if (document.readyState != "loading") fn();
79+
else document.addEventListener("DOMContentLoaded", fn);
80+
})();
81+
</script>
82+
83+
</body>
84+
85+
</html>

pandas_examples/pandas_books.csv

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
,Books,Unnamed: 1,Unnamed: 2,Unnamed: 3,Unnamed: 4,Unnamed: 5,Unnamed: 6
2+
0,Title,Author,Publisher,Publishing Date,ISBN,,
3+
1,Python 101,Mike Driscoll,Mouse vs Python,2020,1234567890,,
4+
2,wxPython Recipes,Mike Driscoll,Apress,2018,978-1-4842-3237-8,,
5+
3,Python Interviews,Mike Driscoll,Packt Publishing,2018,9781788399081,,

sample.wav

3.95 KB
Binary file not shown.

0 commit comments

Comments
 (0)