pyjack-blackjack 2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyjack_blackjack-2/LICENSE +21 -0
- pyjack_blackjack-2/PKG-INFO +51 -0
- pyjack_blackjack-2/README.md +37 -0
- pyjack_blackjack-2/pyproject.toml +29 -0
- pyjack_blackjack-2/setup.cfg +4 -0
- pyjack_blackjack-2/src/pyjack.py +350 -0
- pyjack_blackjack-2/src/pyjack_blackjack.egg-info/PKG-INFO +51 -0
- pyjack_blackjack-2/src/pyjack_blackjack.egg-info/SOURCES.txt +9 -0
- pyjack_blackjack-2/src/pyjack_blackjack.egg-info/dependency_links.txt +1 -0
- pyjack_blackjack-2/src/pyjack_blackjack.egg-info/entry_points.txt +2 -0
- pyjack_blackjack-2/src/pyjack_blackjack.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ConsciousBone
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyjack-blackjack
|
|
3
|
+
Version: 2
|
|
4
|
+
Summary: A simple blackjack game, but in a single Python file... How revolutionary!
|
|
5
|
+
Author-email: ConsciousBone <eplant@consciousb.one>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ConsciousBone/PyJack
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# PyJack</h1>
|
|
16
|
+

|
|
17
|
+
It's blackjack, but in a single Python file.
|
|
18
|
+
Created for Hack Club's Stardance!
|
|
19
|
+
It runs almost anywhere you can run Python (I've not tested this claim but there's nothing really stopping it!), and requires such low system specs that I think the toaster sitting in your kitchen could comfortably run it.
|
|
20
|
+
|
|
21
|
+
# Demos
|
|
22
|
+
## Screenshots
|
|
23
|
+
<img src="/screenshots/1.png" width=384> <img src="/screenshots/2.png" width=384>
|
|
24
|
+
<img src="/screenshots/3.png" width=384> <img src="/screenshots/4.png" width=384>
|
|
25
|
+
## Video
|
|
26
|
+
[YouTube video (unlisted)](https://youtu.be/rOiauafCkaA)
|
|
27
|
+
|
|
28
|
+
# How to play
|
|
29
|
+
1. Download Python from [this page](https://www.python.org/downloads/) if you don't already have it installed.
|
|
30
|
+
2. Download the latest release of PyJack from [this page](https://github.com/ConsciousBone/PyJack/releases/).
|
|
31
|
+
3. Run the downloaded `pyjack.py` file. All the required JSON files will be automatically created in a new `.pyjack_data` directory inside your home folder!
|
|
32
|
+
4. Play the game! Only the keyboard is required for this. You navigate around the menus using the number keys, and actually control the game using H and S to hit or stand. Simple!
|
|
33
|
+
|
|
34
|
+
# Why?
|
|
35
|
+
Why not?
|
|
36
|
+
|
|
37
|
+
# No but seriously, why?
|
|
38
|
+
Blackjack is a pretty good time-waster game, and for those times where you've got nothing to do, why not play some blackjack against a computer? It was also a fun challenge to test my (admittedly kind of lacking) Python skills.
|
|
39
|
+
|
|
40
|
+
# Any future plans for this?
|
|
41
|
+
Yes! In the future I'd like to implement a GUI of some sorts and a money system, however due to my great time management skills, I've left most of this to the last day of Stardance, therefore leaving me with no time to actually implement such features...
|
|
42
|
+
If you want to have a go at implementing these yourself, why not fork this repo and see if you can add some stuff into this simple game?
|
|
43
|
+
|
|
44
|
+
# Tools I used
|
|
45
|
+
- Google
|
|
46
|
+
- ChatGPT (to fix some small bugs, help out with understanding blackjack's rules, and help out with understanding how to publish this on PyPI)
|
|
47
|
+
- Wikipedia (to copy-paste the fancy Unicode symbols used for the cards)
|
|
48
|
+
- VS Code (to write the damn thing)
|
|
49
|
+
|
|
50
|
+
# Something's broken?
|
|
51
|
+
Make a GitHub issue on [this page](https://github.com/ConsciousBone/PyJack/issues), or DM me on Slack and I'll investigate the issue as soon as possible!
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# PyJack</h1>
|
|
2
|
+

|
|
3
|
+
It's blackjack, but in a single Python file.
|
|
4
|
+
Created for Hack Club's Stardance!
|
|
5
|
+
It runs almost anywhere you can run Python (I've not tested this claim but there's nothing really stopping it!), and requires such low system specs that I think the toaster sitting in your kitchen could comfortably run it.
|
|
6
|
+
|
|
7
|
+
# Demos
|
|
8
|
+
## Screenshots
|
|
9
|
+
<img src="/screenshots/1.png" width=384> <img src="/screenshots/2.png" width=384>
|
|
10
|
+
<img src="/screenshots/3.png" width=384> <img src="/screenshots/4.png" width=384>
|
|
11
|
+
## Video
|
|
12
|
+
[YouTube video (unlisted)](https://youtu.be/rOiauafCkaA)
|
|
13
|
+
|
|
14
|
+
# How to play
|
|
15
|
+
1. Download Python from [this page](https://www.python.org/downloads/) if you don't already have it installed.
|
|
16
|
+
2. Download the latest release of PyJack from [this page](https://github.com/ConsciousBone/PyJack/releases/).
|
|
17
|
+
3. Run the downloaded `pyjack.py` file. All the required JSON files will be automatically created in a new `.pyjack_data` directory inside your home folder!
|
|
18
|
+
4. Play the game! Only the keyboard is required for this. You navigate around the menus using the number keys, and actually control the game using H and S to hit or stand. Simple!
|
|
19
|
+
|
|
20
|
+
# Why?
|
|
21
|
+
Why not?
|
|
22
|
+
|
|
23
|
+
# No but seriously, why?
|
|
24
|
+
Blackjack is a pretty good time-waster game, and for those times where you've got nothing to do, why not play some blackjack against a computer? It was also a fun challenge to test my (admittedly kind of lacking) Python skills.
|
|
25
|
+
|
|
26
|
+
# Any future plans for this?
|
|
27
|
+
Yes! In the future I'd like to implement a GUI of some sorts and a money system, however due to my great time management skills, I've left most of this to the last day of Stardance, therefore leaving me with no time to actually implement such features...
|
|
28
|
+
If you want to have a go at implementing these yourself, why not fork this repo and see if you can add some stuff into this simple game?
|
|
29
|
+
|
|
30
|
+
# Tools I used
|
|
31
|
+
- Google
|
|
32
|
+
- ChatGPT (to fix some small bugs, help out with understanding blackjack's rules, and help out with understanding how to publish this on PyPI)
|
|
33
|
+
- Wikipedia (to copy-paste the fancy Unicode symbols used for the cards)
|
|
34
|
+
- VS Code (to write the damn thing)
|
|
35
|
+
|
|
36
|
+
# Something's broken?
|
|
37
|
+
Make a GitHub issue on [this page](https://github.com/ConsciousBone/PyJack/issues), or DM me on Slack and I'll investigate the issue as soon as possible!
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyjack-blackjack"
|
|
7
|
+
version = "2"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="ConsciousBone", email="eplant@consciousb.one" },
|
|
10
|
+
]
|
|
11
|
+
description = "A simple blackjack game, but in a single Python file... How revolutionary!"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
]
|
|
18
|
+
license = "MIT"
|
|
19
|
+
license-files = ["LICEN[CS]E*"]
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
Homepage = "https://github.com/ConsciousBone/PyJack"
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
pyjack = "pyjack:main"
|
|
26
|
+
|
|
27
|
+
[tool.setuptools]
|
|
28
|
+
package-dir = {"" = "src"}
|
|
29
|
+
py-modules = ["pyjack"]
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# ░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
2
|
+
# ░█▀█░█░█░▀▀█░█▀█░█▀▀░█░█░
|
|
3
|
+
# ░█▀▀░░█░░░░█░█▀█░█░░░█▀▄░
|
|
4
|
+
# ░▀░░░░▀░░▀▀░░▀░▀░▀▀▀░▀░▀░
|
|
5
|
+
# ░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
6
|
+
# PyJack - Blackjack but in a single Python file!
|
|
7
|
+
# Created by @consciousbone for Hack Club's Stardance
|
|
8
|
+
# ASCII art from https://www.asciiart.eu/text-to-ascii-art
|
|
9
|
+
|
|
10
|
+
# MARK: Imports
|
|
11
|
+
import os
|
|
12
|
+
import random
|
|
13
|
+
import time
|
|
14
|
+
import json
|
|
15
|
+
|
|
16
|
+
# MARK: General constants
|
|
17
|
+
PYJACK_VERSION = 2
|
|
18
|
+
DATA_DIR = os.path.join(os.path.expanduser("~"), ".pyjack_data")
|
|
19
|
+
|
|
20
|
+
PYJACK_STATS_FILENAME = os.path.join(DATA_DIR, "stats.json")
|
|
21
|
+
PYJACK_CONFIG_FILENAME = os.path.join(DATA_DIR, "config.json")
|
|
22
|
+
|
|
23
|
+
# MARK: General variables
|
|
24
|
+
menu_error_header = ""
|
|
25
|
+
|
|
26
|
+
# MARK: General subroutines
|
|
27
|
+
def clear():
|
|
28
|
+
os.system('cls' if os.name == 'nt' else 'clear') # clear terminal
|
|
29
|
+
|
|
30
|
+
def print_logo(page_name):
|
|
31
|
+
config = read_config()
|
|
32
|
+
use_ascii_logo = config.get('show_ascii_art', 1)
|
|
33
|
+
|
|
34
|
+
if use_ascii_logo == 1: # show ascii art
|
|
35
|
+
print(f'''
|
|
36
|
+
░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
37
|
+
░█▀█░█░█░▀▀█░█▀█░█▀▀░█░█░
|
|
38
|
+
░█▀▀░░█░░░░█░█▀█░█░░░█▀▄░
|
|
39
|
+
░▀░░░░▀░░▀▀░░▀░▀░▀▀▀░▀░▀░
|
|
40
|
+
░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
41
|
+
PyJack (v{PYJACK_VERSION})
|
|
42
|
+
{page_name}
|
|
43
|
+
|
|
44
|
+
─────────────────────────
|
|
45
|
+
''')
|
|
46
|
+
else: # don't show ascii art
|
|
47
|
+
print(f"\nPyJack (v{PYJACK_VERSION})\n{page_name}\n\n─────────────────────────\n")
|
|
48
|
+
|
|
49
|
+
# MARK: Data subroutines/functions
|
|
50
|
+
# Partially stolen from another thing I wrote, https://github.com/R002441/HackTrack
|
|
51
|
+
def initialise_files():
|
|
52
|
+
os.makedirs(DATA_DIR, exist_ok=True)
|
|
53
|
+
|
|
54
|
+
if not os.path.exists(PYJACK_STATS_FILENAME): # write default stats file if it doesn't exist
|
|
55
|
+
default_stats = {
|
|
56
|
+
"games_won": 0,
|
|
57
|
+
"games_lost": 0,
|
|
58
|
+
"games_drew": 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
with open(PYJACK_STATS_FILENAME, "w", encoding="utf-8") as file:
|
|
62
|
+
json.dump(default_stats, file, indent=4)
|
|
63
|
+
|
|
64
|
+
if not os.path.exists(PYJACK_CONFIG_FILENAME): # write default config file if it doesn't exist
|
|
65
|
+
default_config = {
|
|
66
|
+
"show_ascii_art": 1,
|
|
67
|
+
"dealer_hits_delay": 1
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
with open(PYJACK_CONFIG_FILENAME, "w", encoding="utf-8") as file:
|
|
71
|
+
json.dump(default_config, file, indent=4)
|
|
72
|
+
|
|
73
|
+
def read_stats():
|
|
74
|
+
if not os.path.exists(PYJACK_STATS_FILENAME):
|
|
75
|
+
return {}
|
|
76
|
+
with open(PYJACK_STATS_FILENAME, "r") as file:
|
|
77
|
+
return json.load(file) # read json file
|
|
78
|
+
|
|
79
|
+
def write_stats(stats):
|
|
80
|
+
with open(PYJACK_STATS_FILENAME, "w") as file:
|
|
81
|
+
json.dump(stats, file, indent=2) # write back into json
|
|
82
|
+
|
|
83
|
+
def read_config():
|
|
84
|
+
if not os.path.exists(PYJACK_CONFIG_FILENAME):
|
|
85
|
+
return {}
|
|
86
|
+
with open(PYJACK_CONFIG_FILENAME, "r") as file:
|
|
87
|
+
return json.load(file) # read json file
|
|
88
|
+
|
|
89
|
+
def write_config(config):
|
|
90
|
+
with open(PYJACK_CONFIG_FILENAME, "w") as file:
|
|
91
|
+
json.dump(config, file, indent=2) # write back into json
|
|
92
|
+
|
|
93
|
+
# MARK: Menu subroutines
|
|
94
|
+
def menu_header(page_name):
|
|
95
|
+
global menu_error_header # let this subroutine use that var
|
|
96
|
+
|
|
97
|
+
clear() # very clean very fancy very wow
|
|
98
|
+
print_logo(page_name)
|
|
99
|
+
|
|
100
|
+
if menu_error_header != "": # theres an error which needs showing, do it!
|
|
101
|
+
print(f"{menu_error_header}\n")
|
|
102
|
+
menu_error_header = "" # clear it to ensure it doesn't keep showing
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def main_menu():
|
|
106
|
+
while True:
|
|
107
|
+
global menu_error_header # let this subroutine use that var
|
|
108
|
+
menu_header("Main menu")
|
|
109
|
+
print('''\
|
|
110
|
+
1) Play
|
|
111
|
+
2) Stats
|
|
112
|
+
3) Config
|
|
113
|
+
4) Quit
|
|
114
|
+
''')
|
|
115
|
+
|
|
116
|
+
selected_menu_option = input("Select an option.\n> ") # get input
|
|
117
|
+
if selected_menu_option == "1": # play
|
|
118
|
+
while True: # loop
|
|
119
|
+
play_blackjack()
|
|
120
|
+
|
|
121
|
+
while True: # prevents the else case from starting another game
|
|
122
|
+
play_again = input("Play again (Y/n)?\n> ")
|
|
123
|
+
if play_again.lower() == "y": # play again
|
|
124
|
+
clear() # clear screen so terminal isnt cluttered with previous game
|
|
125
|
+
break # play again
|
|
126
|
+
elif play_again.lower() == "n": # don't play again, back to menu
|
|
127
|
+
clear()
|
|
128
|
+
break
|
|
129
|
+
elif play_again.strip() == "": # user inputted nothing, default to yes
|
|
130
|
+
clear()
|
|
131
|
+
break
|
|
132
|
+
else: # user inputted something else entirely
|
|
133
|
+
print("Invalid option.")
|
|
134
|
+
|
|
135
|
+
if play_again.lower() == "n": # don't play again, back to menu
|
|
136
|
+
break # exit the play again loop
|
|
137
|
+
elif selected_menu_option == "2": # stats
|
|
138
|
+
stats_menu()
|
|
139
|
+
elif selected_menu_option == "3": # config
|
|
140
|
+
config_menu()
|
|
141
|
+
elif selected_menu_option == "4": # quit
|
|
142
|
+
print("Thanks for playing!")
|
|
143
|
+
break
|
|
144
|
+
else:
|
|
145
|
+
menu_error_header = "Invalid option."
|
|
146
|
+
|
|
147
|
+
def stats_menu():
|
|
148
|
+
while True:
|
|
149
|
+
global menu_error_header
|
|
150
|
+
stats = read_stats()
|
|
151
|
+
|
|
152
|
+
menu_header("Stats")
|
|
153
|
+
|
|
154
|
+
print(f"Games won: {stats['games_won']}")
|
|
155
|
+
print(f"Games lost: {stats['games_lost']}")
|
|
156
|
+
print(f"Games drew: {stats['games_drew']}")
|
|
157
|
+
|
|
158
|
+
print() # blank line for seperation
|
|
159
|
+
print('''\
|
|
160
|
+
1) Back
|
|
161
|
+
''')
|
|
162
|
+
|
|
163
|
+
selected_menu_option = input("Select an option.\n> ")
|
|
164
|
+
if selected_menu_option == "1":
|
|
165
|
+
return
|
|
166
|
+
else:
|
|
167
|
+
menu_error_header = "Invalid option."
|
|
168
|
+
|
|
169
|
+
def config_menu():
|
|
170
|
+
while True:
|
|
171
|
+
global menu_error_header
|
|
172
|
+
config = read_config()
|
|
173
|
+
|
|
174
|
+
menu_header("Config")
|
|
175
|
+
|
|
176
|
+
print("Current config:") # there's probably a much better way to do this, but this is easy :)
|
|
177
|
+
if config.get('show_ascii_art', 1) == 1:
|
|
178
|
+
print("Show ASCII art: Yes")
|
|
179
|
+
elif config.get('show_ascii_art', 1) == 0:
|
|
180
|
+
print("Show ASCII art: No")
|
|
181
|
+
if config.get('dealer_hits_delay', 1) == 1:
|
|
182
|
+
print("Dealer has delay when hitting: Yes")
|
|
183
|
+
elif config.get('dealer_hits_delay', 1) == 0:
|
|
184
|
+
print("Dealer has delay when hitting: No")
|
|
185
|
+
|
|
186
|
+
print() # blank line for seperation
|
|
187
|
+
print('''\
|
|
188
|
+
1) Toggle "Show ASCII art"
|
|
189
|
+
2) Toggle "Dealer has delay when hitting"
|
|
190
|
+
3) Back
|
|
191
|
+
''')
|
|
192
|
+
|
|
193
|
+
selected_menu_option = input("Select an option.\n> ")
|
|
194
|
+
if selected_menu_option == "1":
|
|
195
|
+
if config.get('show_ascii_art', 1) == 1:
|
|
196
|
+
config['show_ascii_art'] = 0
|
|
197
|
+
write_config(config)
|
|
198
|
+
menu_error_header = "Toggled \"Show ASCII art\"."
|
|
199
|
+
|
|
200
|
+
elif config.get('show_ascii_art', 1) == 0:
|
|
201
|
+
config['show_ascii_art'] = 1
|
|
202
|
+
write_config(config)
|
|
203
|
+
menu_error_header = "Toggled \"Show ASCII art\"."
|
|
204
|
+
|
|
205
|
+
elif selected_menu_option == "2":
|
|
206
|
+
if config.get('dealer_hits_delay', 1) == 1:
|
|
207
|
+
config['dealer_hits_delay'] = 0
|
|
208
|
+
write_config(config)
|
|
209
|
+
menu_error_header = "Toggled \"Dealer has delay when hitting\"."
|
|
210
|
+
|
|
211
|
+
elif config.get('dealer_hits_delay', 1) == 0:
|
|
212
|
+
config['dealer_hits_delay'] = 1
|
|
213
|
+
write_config(config)
|
|
214
|
+
menu_error_header = "Toggled \"Dealer has delay when hitting\"."
|
|
215
|
+
|
|
216
|
+
elif selected_menu_option == "3":
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
else:
|
|
220
|
+
menu_error_header = "Invalid option."
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# MARK: Blackjack constants
|
|
224
|
+
SUITS = ["♠", "♥", "♦", "♣"] # ooh fancy unicode thanks to https://en.wikipedia.org/wiki/Playing_cards_in_Unicode
|
|
225
|
+
RANKS = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
|
|
226
|
+
|
|
227
|
+
# MARK: Blackjack variables
|
|
228
|
+
deck = []
|
|
229
|
+
|
|
230
|
+
# MARK: Blackjack subroutines/functions
|
|
231
|
+
def build_deck():
|
|
232
|
+
deck.clear() # make sure deck doesn't end up massive
|
|
233
|
+
for suit in SUITS:
|
|
234
|
+
for rank in RANKS:
|
|
235
|
+
deck.append((rank, suit))
|
|
236
|
+
random.shuffle(deck)
|
|
237
|
+
|
|
238
|
+
def hand_value(hand):
|
|
239
|
+
value = 0
|
|
240
|
+
aces = 0
|
|
241
|
+
|
|
242
|
+
for rank, suit in hand:
|
|
243
|
+
if rank in ["J", "Q", "K"]: # handle the cards that aren't numbers, aces come later
|
|
244
|
+
value += 10
|
|
245
|
+
elif rank == "A": # now handle aces part 1; default to 11, but will move to 1 later if it would cause a bust
|
|
246
|
+
value += 11
|
|
247
|
+
aces += 1 # keep track of number of aces for later
|
|
248
|
+
else:
|
|
249
|
+
value += int(rank) # ez
|
|
250
|
+
|
|
251
|
+
while value > 21 and aces > 0: # handle aces part 2; turn aces one by one from 11 to 1
|
|
252
|
+
value -= 10
|
|
253
|
+
aces -= 1
|
|
254
|
+
|
|
255
|
+
return value # finished counting!
|
|
256
|
+
|
|
257
|
+
def show_hand(name, hand, hide_first_card=False):
|
|
258
|
+
if hide_first_card: # hide the first card, used with dealer
|
|
259
|
+
cards = ["??"] + [f"{rank}{suit}" for rank, suit in hand[1:]] # skip first card since it's hidden
|
|
260
|
+
print(f"{name}: {' '.join(cards)}") # make sure cards are formatted nicely
|
|
261
|
+
else:
|
|
262
|
+
cards = [f"{rank}{suit}" for rank, suit in hand]
|
|
263
|
+
print(f"{name}: {' '.join(cards)} ({hand_value(hand)})") # also show hand value for player
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def play_blackjack(): # the main event 0.0
|
|
267
|
+
stats = read_stats()
|
|
268
|
+
config = read_config()
|
|
269
|
+
|
|
270
|
+
build_deck() # get a deck
|
|
271
|
+
menu_header("Play")
|
|
272
|
+
|
|
273
|
+
player = [deck.pop(), deck.pop()] # give player 2 cards
|
|
274
|
+
dealer = [deck.pop(), deck.pop()] # give dealer 2 cards
|
|
275
|
+
|
|
276
|
+
while True: # player's turn!
|
|
277
|
+
show_hand("Dealer", dealer, hide_first_card=True) # show dealer's hand, hiding first card
|
|
278
|
+
show_hand("Player", player) # show player's hand, ofc not hiding your own cards :/
|
|
279
|
+
|
|
280
|
+
player_value = hand_value(player)
|
|
281
|
+
if player_value == 21: # player is at blackjack, end
|
|
282
|
+
if len(player) == 2:
|
|
283
|
+
print("Blackjack!\n")
|
|
284
|
+
else:
|
|
285
|
+
print("21!\n")
|
|
286
|
+
break
|
|
287
|
+
if player_value > 21: # player is bust, end
|
|
288
|
+
stats['games_lost'] = stats['games_lost'] + 1
|
|
289
|
+
write_stats(stats)
|
|
290
|
+
print("Bust, you lose!\n")
|
|
291
|
+
return
|
|
292
|
+
|
|
293
|
+
print("") # blank line for visual seperation
|
|
294
|
+
choice = input("(H)it or (S)tand?\n> ")
|
|
295
|
+
if choice.lower() == "h": # hit; resiliant to PEBCAK errors (google it :D)
|
|
296
|
+
player.append(deck.pop()) # give player another card
|
|
297
|
+
elif choice.lower() == "s": # stand; also resiliant to PEBCAK errors
|
|
298
|
+
break # exit while loop, dealer's turn!
|
|
299
|
+
else:
|
|
300
|
+
print("Invalid option.")
|
|
301
|
+
|
|
302
|
+
print("") # another blank line for seperation
|
|
303
|
+
|
|
304
|
+
# dealer's turn!
|
|
305
|
+
print("\nDealer's turn!\n")
|
|
306
|
+
|
|
307
|
+
show_hand("Dealer", dealer) # show dealer's hand, *not* hiding first card
|
|
308
|
+
show_hand("Player", player) # show player's hand, ofc not hiding your own cards :/
|
|
309
|
+
|
|
310
|
+
while hand_value(dealer) < 17: # dealer must stand on 17
|
|
311
|
+
if config.get('dealer_hits_delay', 1) == 1: # does config allow for a delay
|
|
312
|
+
time.sleep(random.uniform(0.5, 1)) # wait for random amount of time between 0.5s and 1s
|
|
313
|
+
|
|
314
|
+
print("") # blank line for seperation
|
|
315
|
+
print("Dealer hits!")
|
|
316
|
+
dealer.append(deck.pop()) # give dealer another card
|
|
317
|
+
|
|
318
|
+
print("")
|
|
319
|
+
show_hand("Dealer", dealer) # show dealer's hand, *not* hiding first card
|
|
320
|
+
show_hand("Player", player) # show player's hand, ofc not hiding your own cards :/
|
|
321
|
+
|
|
322
|
+
dealer_value = hand_value(dealer)
|
|
323
|
+
player_value = hand_value(player)
|
|
324
|
+
|
|
325
|
+
if dealer_value > 21: # dealer is bust :D
|
|
326
|
+
stats['games_won'] = stats['games_won'] + 1
|
|
327
|
+
write_stats(stats)
|
|
328
|
+
print("\nDealer bust!")
|
|
329
|
+
elif dealer_value > player_value: # dealer wins
|
|
330
|
+
stats['games_lost'] = stats['games_lost'] + 1
|
|
331
|
+
write_stats(stats)
|
|
332
|
+
print("\nDealer wins!")
|
|
333
|
+
elif dealer_value < player_value: # player wins
|
|
334
|
+
stats['games_won'] = stats['games_won'] + 1
|
|
335
|
+
write_stats(stats)
|
|
336
|
+
print("\nYou win!")
|
|
337
|
+
else: # draw
|
|
338
|
+
stats['games_drew'] = stats['games_drew'] + 1
|
|
339
|
+
write_stats(stats)
|
|
340
|
+
print("\nIt\'s a draw!")
|
|
341
|
+
|
|
342
|
+
print("")
|
|
343
|
+
|
|
344
|
+
# MARK: Run the damn thing
|
|
345
|
+
def main():
|
|
346
|
+
initialise_files() # create data files if they don't exist
|
|
347
|
+
main_menu() # run main menu
|
|
348
|
+
|
|
349
|
+
if __name__ == "__main__":
|
|
350
|
+
main()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyjack-blackjack
|
|
3
|
+
Version: 2
|
|
4
|
+
Summary: A simple blackjack game, but in a single Python file... How revolutionary!
|
|
5
|
+
Author-email: ConsciousBone <eplant@consciousb.one>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ConsciousBone/PyJack
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# PyJack</h1>
|
|
16
|
+

|
|
17
|
+
It's blackjack, but in a single Python file.
|
|
18
|
+
Created for Hack Club's Stardance!
|
|
19
|
+
It runs almost anywhere you can run Python (I've not tested this claim but there's nothing really stopping it!), and requires such low system specs that I think the toaster sitting in your kitchen could comfortably run it.
|
|
20
|
+
|
|
21
|
+
# Demos
|
|
22
|
+
## Screenshots
|
|
23
|
+
<img src="/screenshots/1.png" width=384> <img src="/screenshots/2.png" width=384>
|
|
24
|
+
<img src="/screenshots/3.png" width=384> <img src="/screenshots/4.png" width=384>
|
|
25
|
+
## Video
|
|
26
|
+
[YouTube video (unlisted)](https://youtu.be/rOiauafCkaA)
|
|
27
|
+
|
|
28
|
+
# How to play
|
|
29
|
+
1. Download Python from [this page](https://www.python.org/downloads/) if you don't already have it installed.
|
|
30
|
+
2. Download the latest release of PyJack from [this page](https://github.com/ConsciousBone/PyJack/releases/).
|
|
31
|
+
3. Run the downloaded `pyjack.py` file. All the required JSON files will be automatically created in a new `.pyjack_data` directory inside your home folder!
|
|
32
|
+
4. Play the game! Only the keyboard is required for this. You navigate around the menus using the number keys, and actually control the game using H and S to hit or stand. Simple!
|
|
33
|
+
|
|
34
|
+
# Why?
|
|
35
|
+
Why not?
|
|
36
|
+
|
|
37
|
+
# No but seriously, why?
|
|
38
|
+
Blackjack is a pretty good time-waster game, and for those times where you've got nothing to do, why not play some blackjack against a computer? It was also a fun challenge to test my (admittedly kind of lacking) Python skills.
|
|
39
|
+
|
|
40
|
+
# Any future plans for this?
|
|
41
|
+
Yes! In the future I'd like to implement a GUI of some sorts and a money system, however due to my great time management skills, I've left most of this to the last day of Stardance, therefore leaving me with no time to actually implement such features...
|
|
42
|
+
If you want to have a go at implementing these yourself, why not fork this repo and see if you can add some stuff into this simple game?
|
|
43
|
+
|
|
44
|
+
# Tools I used
|
|
45
|
+
- Google
|
|
46
|
+
- ChatGPT (to fix some small bugs, help out with understanding blackjack's rules, and help out with understanding how to publish this on PyPI)
|
|
47
|
+
- Wikipedia (to copy-paste the fancy Unicode symbols used for the cards)
|
|
48
|
+
- VS Code (to write the damn thing)
|
|
49
|
+
|
|
50
|
+
# Something's broken?
|
|
51
|
+
Make a GitHub issue on [this page](https://github.com/ConsciousBone/PyJack/issues), or DM me on Slack and I'll investigate the issue as soon as possible!
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/pyjack.py
|
|
5
|
+
src/pyjack_blackjack.egg-info/PKG-INFO
|
|
6
|
+
src/pyjack_blackjack.egg-info/SOURCES.txt
|
|
7
|
+
src/pyjack_blackjack.egg-info/dependency_links.txt
|
|
8
|
+
src/pyjack_blackjack.egg-info/entry_points.txt
|
|
9
|
+
src/pyjack_blackjack.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyjack
|