Advertisement
Windspar

Pygame Dice. Start of a yahtzee game.

Dec 1st, 2022
935
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.47 KB | None | 0 0
  1. import pygame
  2. import random
  3.  
  4. from itertools import count
  5. from pygame.sprite import Group, Sprite
  6.  
  7. # Interface
  8. class State:
  9.     def __init__(self, engine):
  10.         self.engine = engine
  11.  
  12.     def on_draw(self, surface): pass
  13.     def on_event(self, event): pass
  14.     def on_update(self, delta, ticks): pass
  15.  
  16. # Engine
  17. class DisplayEngine:
  18.     def __init__(self, caption, width, height, flags=0):
  19.         # Basic Pygame Setup
  20.         pygame.display.set_caption(caption)
  21.         self.surface = pygame.display.set_mode((width, height), flags)
  22.         self.rect = self.surface.get_rect()
  23.         self.clock = pygame.time.Clock()
  24.         self.running = True
  25.         self.delta = 0
  26.         self.fps = 60
  27.  
  28.         # Handle what state is active.
  29.         self._state = State(self)
  30.         self.next_state = None
  31.  
  32.     # Main Loop
  33.     def run(self, state=None):
  34.         if state:
  35.             self._state = state
  36.  
  37.         while self.running:
  38.             # State Changer
  39.             if self.next_state:
  40.                 self._state = self.next_state
  41.                 self.next_state = None
  42.  
  43.             # Event Loop
  44.             for event in pygame.event.get():
  45.                 # Just send the events to state to handle
  46.                 self._state.on_event(event)
  47.  
  48.             ticks = pygame.time.get_ticks()
  49.             self._state.on_draw(self.surface)
  50.             self._state.on_update(self.delta, ticks)
  51.             pygame.display.flip()
  52.             self.delta = self.clock.tick(self.fps)
  53.  
  54.     def quit(self):
  55.         self.running = False
  56.  
  57. class SimpleSprite(Sprite):
  58.     def __init__(self, image):
  59.         super().__init__()
  60.         self.image = image
  61.         self.rect = image.get_rect()
  62.  
  63. class Callback:
  64.     def __init__(self, callback, user_data=None):
  65.         self.parent = None
  66.         self.callback = callback
  67.         self.user_data = user_data
  68.  
  69.     def link(self, parent):
  70.         self.parent = parent
  71.         return self
  72.  
  73.     def call(self):
  74.         self.callback(self)
  75.  
  76. class Pen:
  77.     def __init__(self, font, color):
  78.         self.font = font
  79.         self.color = color
  80.  
  81.     def render(self, text):
  82.         return self.font.render(text, 1, self.color)
  83.  
  84.     def write(self, text):
  85.         return SimpleSprite(self.render(text))
  86.  
  87. class ButtonImages:
  88.     def __init__(self, normal, hover, pushed):
  89.         self.normal = normal
  90.         self.hover = hover
  91.         self.pushed = pushed
  92.  
  93. class ButtonGroup:
  94.     def __init__(self):
  95.         self.buttons = Group()
  96.         self.text = Group()
  97.  
  98.     def add(self, button):
  99.         self.buttons.add(button)
  100.         self.text.add(button.tsprite)
  101.  
  102.     def draw(self, surface):
  103.         self.buttons.draw(surface)
  104.         self.text.draw(surface)
  105.  
  106. class Button(Sprite):
  107.     def __init__(self, images, pen, text, position, callback):
  108.         super().__init__()
  109.         self.pen = pen
  110.         self.images = images
  111.         self.image = images.normal
  112.         self.rect = self.images.normal.get_rect(topleft=position)
  113.  
  114.         self.is_mouse_over = False
  115.         self.is_pushed = False
  116.         self.callback = callback.link(self)
  117.         self.update_text(text)
  118.  
  119.     def mouse_over(self, event):
  120.         self.is_mouse_over = self.rect.collidepoint(event.pos)
  121.         self.update_image()
  122.  
  123.     def click(self, event):
  124.         self.mouse_over(event)
  125.         if self.is_mouse_over:
  126.             #self.is_pushed = False
  127.             self.callback.call()
  128.  
  129.     def update_image(self):
  130.         if self.is_pushed:
  131.             self.image = self.images.pushed
  132.         elif self.is_mouse_over:
  133.             self.image = self.images.hover
  134.         else:
  135.             self.image = self.images.normal
  136.  
  137.     def update_text(self, text):
  138.         self.tsprite = self.pen.write(text)
  139.         self.tsprite.rect.center = self.rect.center
  140.  
  141. # Store images. Because you want to create/load, convert, and/or size once.
  142. class Images:
  143.     def __init__(self):
  144.         self.dice = self.create_dice(40, 'snow', 'black', 3)
  145.         self.hdice = self.create_dice(40, 'skyblue', 'black', 3)
  146.         self.sdice = self.create_dice(40, 'lightgreen', 'black', 3)
  147.  
  148.         self.button_images = self.create_button((100, 30), 'blue', 'dodgerblue', 'navy')
  149.  
  150.     def create_dice(self, size, color, dot_color, dot_size):
  151.         surface = pygame.Surface((size, size))
  152.         surface.fill(color)
  153.         rect = surface.get_rect()
  154.         inner = rect.inflate(-rect.centerx, -rect.centery)
  155.         images = []
  156.  
  157.         for i in range(1, 7):
  158.             s = surface.copy()
  159.             if i in [1, 3, 5]:
  160.                 pygame.draw.circle(s, dot_color, rect.center, dot_size)
  161.  
  162.             if i != 1: # 2-6
  163.                 pygame.draw.circle(s, dot_color, inner.topleft, dot_size)
  164.                 pygame.draw.circle(s, dot_color, inner.bottomright, dot_size)
  165.  
  166.             if i > 3:
  167.                 pygame.draw.circle(s, dot_color, inner.topright, dot_size)
  168.                 pygame.draw.circle(s, dot_color, inner.bottomleft, dot_size)
  169.  
  170.             if i == 6:
  171.                 pygame.draw.circle(s, dot_color, inner.midleft, dot_size)
  172.                 pygame.draw.circle(s, dot_color, inner.midright, dot_size)
  173.  
  174.             images.append(s)
  175.         return images
  176.  
  177.     def create_button(self, size, color, hcolor, pcolor):
  178.         surface = pygame.Surface(size)
  179.         surface.fill(color)
  180.         hsurface = surface.copy()
  181.         hsurface.fill(hcolor)
  182.         psurface = surface.copy()
  183.         psurface.fill(pcolor)
  184.         return ButtonImages(surface, hsurface, psurface)
  185.  
  186. class Die(Sprite):
  187.     def __init__(self, images, position):
  188.         super().__init__()
  189.         # These are just reference
  190.         self.images = images
  191.         self.rect = images[0][0].get_rect(topleft=position)
  192.         self.is_mouse_over = False
  193.         self.is_selected = False
  194.         self.roll()
  195.  
  196.     def roll(self):
  197.         self.value = random.randint(1, 6)
  198.         self.is_selected = False
  199.         self.update_image()
  200.  
  201.     def mouse_over(self, mpos):
  202.         self.is_mouse_over = self.rect.collidepoint(mpos)
  203.         self.update_image()
  204.  
  205.     def selected(self):
  206.         if self.is_mouse_over:
  207.             self.is_selected = not self.is_selected
  208.             self.update_image()
  209.  
  210.     def update_image(self):
  211.         if self.is_selected:
  212.             self.image = self.images[2][self.value - 1]
  213.         elif self.is_mouse_over:
  214.             self.image = self.images[1][self.value - 1]
  215.         else:
  216.             self.image = self.images[0][self.value - 1]
  217.  
  218. class DisplayLines:
  219.     def __init__(self, pen, position):
  220.         self.position = position
  221.         self.lines = []
  222.         self.pen = pen
  223.  
  224.     def add(self, text):
  225.         self.lines.append(self.pen.render(text))
  226.  
  227.     def clear(self):
  228.         self.lines = []
  229.  
  230.     def draw(self, surface):
  231.         y = count(self.position[1], self.pen.font.get_linesize() + 2)
  232.         for line in self.lines:
  233.             surface.blit(line, (self.position[0], next(y)))
  234.  
  235. class GameState(State):
  236.     def __init__(self, engine):
  237.         super().__init__(engine)
  238.         self.dice_space = 50
  239.         self.dice_fromedgex = 30
  240.         self.dice_fromedgey = 30
  241.  
  242.         self.images = Images()
  243.         self.roll_button = self.create_roll_button()
  244.         self.bgroup = ButtonGroup()
  245.         self.dice = Group()
  246.         self.set_up()
  247.  
  248.     def create_roll_button(self):
  249.         pen = Pen(pygame.font.Font(None, 28), 'snow')
  250.         pos = self.dice_space * 5 - 100 + self.dice_fromedgex, 80
  251.         callback = Callback(self.push_rolled)
  252.         return Button(self.images.button_images, pen, "Roll", pos, callback)
  253.  
  254.     def set_up(self):
  255.         images = self.images.dice, self.images.hdice, self.images.sdice
  256.         for i in range(5):
  257.             pos = self.dice_space * i + self.dice_fromedgex, self.dice_fromedgey
  258.             self.dice.add(Die(images, pos))
  259.  
  260.         self.bgroup.add(self.roll_button)
  261.         pos = self.engine.rect.centerx, 10
  262.         pen = Pen(pygame.font.Font(None, 22), 'lawngreen')
  263.         self.lines = DisplayLines(pen, pos)
  264.         self.get_data()
  265.  
  266.     def push_rolled(self, callback):
  267.         for d in self.dice:
  268.             if d.is_selected:
  269.                 d.roll()
  270.  
  271.         self.get_data()
  272.  
  273.     def get_data(self):
  274.         self.lines.clear()
  275.         values = [d.value for d in self.dice]
  276.         set_v = set(values)
  277.         if len(set_v) == 1:
  278.             self.lines.add("Yahtzee")
  279.             self.lines.add("4 of a kind")
  280.             self.lines.add("3 of a kind")
  281.  
  282.         if len(set_v) == 2:
  283.             for v in set_v:
  284.                 if values.count(v) == 4:
  285.                     self.lines.add("4 of a kind")
  286.                 elif values.count(v) == 3:
  287.                     self.lines.add("Fullhouse")
  288.  
  289.             self.lines.add("3 of a kind")
  290.  
  291.         if len(set_v) == 3:
  292.             data = [values.count(i) for i in set_v]
  293.             if 3 in data:
  294.                 self.lines.add("3 of a kind")
  295.  
  296.         if len(set_v) == 4:
  297.             data = list(set_v)
  298.             data.sort()
  299.             if ([1, 2, 3, 4] == data or
  300.                 [2, 3, 4, 5] == data or
  301.                 [3, 4, 5, 6] == data):
  302.  
  303.                 self.lines.add("small straight")
  304.  
  305.         if len(set_v) == 5:
  306.             data = list(set_v)
  307.             data.sort()
  308.             if [1, 2, 3, 4, 5] == data or [2, 3, 4, 5, 6] == data:
  309.                 self.lines.add("large straight")
  310.  
  311.             if ([1, 2, 3, 4] == data[:-1] or
  312.                 [2, 3, 4, 5] == data[:-1] or
  313.                 [2, 3, 4, 5] == data[1:] or
  314.                 [3, 4, 5, 6] == data[1:]):
  315.  
  316.                 self.lines.add("small straight")
  317.  
  318.     # Override. From state interface
  319.     def on_draw(self, surface):
  320.         surface.fill('gray10')
  321.         self.dice.draw(surface)
  322.         self.bgroup.draw(surface)
  323.         self.lines.draw(surface)
  324.  
  325.     # Override. From state interface
  326.     def on_event(self, event):
  327.         if event.type == pygame.QUIT:
  328.             self.engine.quit()
  329.         elif event.type == pygame.MOUSEMOTION:
  330.             for d in self.dice:
  331.                 d.mouse_over(event.pos)
  332.  
  333.             for b in self.bgroup.buttons:
  334.                 b.mouse_over(event)
  335.  
  336.         elif event.type == pygame.MOUSEBUTTONDOWN:
  337.             if event.button == 1:
  338.                 self.roll_button.click(event)
  339.                 for d in self.dice:
  340.                     d.selected()
  341.  
  342. if __name__ == '__main__':
  343.     pygame.init()
  344.     engine = DisplayEngine("Dice", 800, 600)
  345.     game = GameState(engine)
  346.     engine.run(game)
  347.     pygame.quit()
  348.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement