I made a 3 reel slot machine using the pygame library in Python. This is what I have so far.
I want to improve it to make it work for a 3x5 slot machine with multiple paylines, right now it is a 1x3 slot machine.
I also want to improve on my programming skills as I am still fairly new to programming and have only been doing it for about a year. If anyone has any recommendations on how to make this code cleaner or more efficient, let me know.
nums.json contains this list of numbers. I made this list of numbers so some icons have more weight than others, hence why some give the player more money.
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
1,
1,
1,
1,
1,
2,
2
]
This is the slotmachine class file.
import random
import json
f = open("nums.json")
nums = json.load(f)
class SlotMachine:
def __init__(self, windict):
self.windict = windict
self.output = ""
def play(self):
num = random.choice(nums)
self.output += str(num)
return num
def checkwin(self):
if self.output in self.windict:
win = str(self.windict[self.output] * 100)
self.output = ""
return win
else:
loss = "0"
self.output = ""
return loss
This is the main Python file.
import pygame
from slotmachine import SlotMachine
import time
pygame.init()
eagles = "The Best of My Love - On the Border (front).jpg"
beatles = "Abbey Road (front).jpg"
pink = "Wish you were here (front).jpg"
(width, height) = (800, 600)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Ryan's Slot Machine")
white = (255, 255, 255)
green = (0, 255, 0)
blue = (0, 0, 128)
screen.fill(white)
bg = pygame.image.load("border.png")
screen.blit(bg, (0, 0))
pygame.display.update()
picdict = {0: eagles, 1: beatles, 2: pink}
positions = ((200, 275), (350, 275), (497, 275))
slot = SlotMachine({"000": 1, "111": 2, "222": 5})
font = pygame.font.Font("freesansbold.ttf", 25)
def icon_to_screen(x, y, path):
icon = pygame.image.load(path)
small_icon = pygame.transform.scale(icon, (100,50))
screen.blit(small_icon, (x, y))
pygame.display.update()
def main():
play = True
while play:
for event in pygame.event.get():
if event.type == pygame.QUIT:
play = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
screen.fill(white)
bg = pygame.image.load("border.png")
screen.blit(bg, (0, 0))
pygame.display.update()
for position in positions:
i = slot.play()
icon_to_screen(position[0], position[1], picdict[i])
time.sleep(.5)
text = font.render(slot.checkwin(), True, green, blue)
screen.blit(text, (670,36))
pygame.display.update()
if __name__ == "__main__":
main()
1 Answer 1
nums.json
should have a better name like weights.json
, and should only contain three numbers: the weights of each slot outcome, as in
[ 10, 5, 2 ]
When you open this file, do so with a context manager.
picdict
shouldn't be a dictionary: you have a simple integer index, so just make it a tuple.
Don't call random.choice
; call random.choices
passing your weights.
Don't make output
stringly-typed. Build it up as a tuple of integers. Similarly, checkwin
(which should be named check_win
) should return an integer and not a string.
The logic in check_win
can be simplified by unconditionally calling get
on windict
with a default of 0
for no winnings. Only reset output
once.
Some of the simple constants like your colours can stay as capitalised constants in the global namespace. The rest of the global code should move into a function. One easy way to achieve this is to move the global code into the __init__
of a class.
play
needs to go away, and you should just use a while True
.
When you check KEYDOWN
you should simultaneously check the key code after an and
.
Add a constant for things like the antialias parameter to render()
.
Suggested
Only partially tested since I don't have your images:
import json
import random
import pygame
import time
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLUE = (0, 0, 128)
ANTIALIAS = True
with open("nums.json") as f:
WEIGHTS = json.load(f)
class SlotMachine:
def __init__(self, win_dict: dict[tuple[int, ...], int]):
self.win_dict = win_dict
self.output: tuple[int, ...] = ()
def play(self) -> int:
num, = random.choices(population=range(3), weights=WEIGHTS)
self.output += num,
return num
def check_win(self) -> int:
winnings = self.win_dict.get(self.output, 0)
self.output = ()
return 100 * winnings
class Game:
WIDTH, HEIGHT = 800, 600
POSITIONS = (200, 275), (350, 275), (497, 275)
def __init__(self) -> None:
pygame.init()
pygame.display.set_caption("Ryan's Slot Machine")
self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT))
self.screen.fill(WHITE)
self.bg = pygame.image.load("border.png")
self.screen.blit(self.bg, (0, 0))
pygame.display.update()
self.slot_pictures = (
"The Best of My Love - On the Border (front).jpg",
"Abbey Road (front).jpg",
"Wish you were here (front).jpg",
)
self.machine = SlotMachine({(0, 0, 0): 1, (1, 1, 1): 2, (2, 2, 2): 5})
self.font = pygame.font.Font("freesansbold.ttf", 25)
def icon_to_screen(self, x: int, y: int, path: str) -> None:
icon = pygame.image.load(path)
small_icon = pygame.transform.scale(icon, (100, 50))
self.screen.blit(small_icon, (x, y))
pygame.display.update()
def run(self) -> None:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
if event.type == pygame.KEYDOWN and event.key == pygame.K_p:
self.screen.fill(WHITE)
self.screen.blit(self.bg, (0, 0))
pygame.display.update()
for x, y in self.POSITIONS:
i = self.machine.play()
self.icon_to_screen(x, y, self.slot_pictures[i])
time.sleep(.5)
winning_string = str(self.machine.check_win())
foreground = GREEN
background = BLUE
text = self.font.render(winning_string, ANTIALIAS, foreground, background)
self.screen.blit(text, (670, 36))
pygame.display.update()
if __name__ == "__main__":
Game().run()
You can further simplify by not keeping an output
and not mutating the slot machine at all; just returning three reels at once; and including the factor of 100 in your winnings dictionary:
import json
import random
import pygame
import time
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLUE = (0, 0, 128)
ANTIALIAS = True
class SlotMachine:
def __init__(self, win_dict: dict[tuple[int, ...], int]):
with open("weights.json") as f:
self.weights = json.load(f)
self.win_dict = win_dict
def play(self) -> list[int]: # reel positions
return random.choices(population=range(3), weights=self.weights, k=3)
def check_win(self, reels: list[int]) -> int:
return self.win_dict.get(tuple(reels), 0)
class Game:
WIDTH, HEIGHT = 800, 600
POSITIONS = (200, 275), (350, 275), (497, 275)
def __init__(self) -> None:
pygame.init()
pygame.display.set_caption("Ryan's Slot Machine")
self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT))
self.screen.fill(WHITE)
self.bg = pygame.image.load("border.png")
self.screen.blit(self.bg, (0, 0))
pygame.display.update()
self.slot_pictures = (
"The Best of My Love - On the Border (front).jpg",
"Abbey Road (front).jpg",
"Wish you were here (front).jpg",
)
self.machine = SlotMachine({
(0, 0, 0): 100,
(1, 1, 1): 200,
(2, 2, 2): 500,
})
self.font = pygame.font.Font("freesansbold.ttf", 25)
def icon_to_screen(self, x: int, y: int, path: str) -> None:
icon = pygame.image.load(path)
small_icon = pygame.transform.scale(icon, (100, 50))
self.screen.blit(small_icon, (x, y))
pygame.display.update()
def run(self) -> None:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
if event.type == pygame.KEYDOWN and event.key == pygame.K_p:
self.screen.fill(WHITE)
self.screen.blit(self.bg, (0, 0))
pygame.display.update()
reels = self.machine.play()
for (x, y), reel in zip(self.POSITIONS, reels):
self.icon_to_screen(x, y, self.slot_pictures[reel])
time.sleep(.5)
winning_string = str(self.machine.check_win(reels))
foreground = GREEN
background = BLUE
text = self.font.render(winning_string, ANTIALIAS, foreground, background)
self.screen.blit(text, (670, 36))
pygame.display.update()
if __name__ == "__main__":
Game().run()