Skip to content

Instantly share code, notes, and snippets.

@jsbain
Created April 12, 2018 09:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jsbain/3dbd60b05e13fa32a497122ff9754d7b to your computer and use it in GitHub Desktop.
Save jsbain/3dbd60b05e13fa32a497122ff9754d7b to your computer and use it in GitHub Desktop.
boggle.py
import ui
import random
from math import radians
import time
from functools import partial
class Die(ui.View):
def __init__(self,faces='ABCDEF',*args,**kwargs):
self.faces=faces
self._face_idx=0
ui.View.__init__(self,*args,**kwargs)
self.flex=''
@property
def letter(self):
return self.faces[self._face_idx]
@property
def face_idx(self):
return self._face_idx
@face_idx.setter
def face_idx(self,value):
if value < len(self.faces):
self._face_idx=value
def draw(self):
w=self.width
corner_frac=0.05 #fractional corner radius for dice itself
face_frac=0.5*0.65 # fractional corner radius of the face
highlight_color='#e4e4db'
shadow_color='#c6c6be'
face_color='#fefaed'
s1=ui.Path.rounded_rect(0,0,w,w,w*corner_frac)
s2=ui.Path.rounded_rect(0,w/2.,w,w/2.,w*corner_frac)
s3=ui.Path.rounded_rect(0,0,w,w,w*face_frac)
ui.set_color(highlight_color)
s1.fill()
ui.set_color(shadow_color)# little darker
s2.fill()
ui.set_color(face_color) # little lighter
s3.fill()
sz=ui.measure_string(self.faces[self.face_idx],font=('Futura',0.5*self.width))
ui.draw_string(self.faces[self.face_idx],font=('Futura',0.5*w),
rect=( (w-sz.width)/2, (w-sz.height)/2,sz.width,sz.height),
color=(.17, .0, .97))
def roll(self):
''' choose a random face. also, transform angle, and size for animation purposes'''
maxsc=1.4 #amount of relative size variation during roll
minsc=0.6
randscale=minsc+(maxsc-minsc)*random.random()
randangle=radians(90*random.randrange(5))
self.face_idx=random.randrange(len(self.faces))
self.transform=ui.Transform.rotation(randangle).concat(
ui.Transform.scale(randscale,randscale) )
#self.set_needs_display()
def straighten(self):
''' set back to default orientation for reading'''
self.transform=ui.Transform()
class BoggleBoard(ui.View):
''' Just a container for dice. auto sizes the dice based on view size
N: grid size (creates NxN grid)
frame: size of board. always adjusts to be square
other View attribs are valid (bg_color, etc)
self.dice: list of Die's
TODO: Take dice dictionary
'''
def __init__(self,N=4,*args,**kwargs):
self.N=N
self.border=0.05
super(BoggleBoard,self).__init__(*args,**kwargs)
dice=[]
for i in range(N*N):
dice.append(Die(['A','B','C','D','E','F'],
frame=self._ind2frame(i)))
self.add_subview(dice[-1])
self.dice=dice
self.flex='wh'
self.shuffle()
def layout(self):
'''resize board, called when view size changes'''
# first, ensure we are square
if self.width>self.height:
self.width=self.height
elif self.height>self.width:
self.height=self.width
# Then, set dice size/positions based on order of self.dice
for ind,d in enumerate(self.dice):
d.frame=self._ind2frame(ind)
def draw(self):
''' TODO: draw semi 3d indentations underneath dice that are visible when rolling'''
pass
def _ind2frame(self, index):
''' generate dice frame rect for a given index
assume row major ordering'''
N=self.N
y,x=divmod(index,N) #gives x,y location in grid units of the given die
b=self.border #fractional border width
W=(self.width-(N+1)*b*self.width/N)/N #die width, accounting for border
return ui.Rect(b*W + x*(1+b)*W, b*W + y*(1+b)*W, W, W)
def shuffle(self,sender=None):
#first, shuffle die locations, then tell each die to roll itself
random.shuffle(self.dice)
for ind,d in enumerate(self.dice):
d.frame=self._ind2frame(ind) # move to new spot on grid
d.roll() #roll it
def letters(self, sender=None):
'''return list of visible letters'''
return [d.letter for d in self.dice]
def solve(self):
''' find best possible score, using internet anagram server,or bogglewords.com, etc'''
pass
def animated_shuffle(self,sender=None, duration=0.5, straighten_after=True):
'''animate a single shuffle.
optionally: reorient dice after shuffling
'''
ui.animate(self.shuffle, duration)
if straighten_after:
ui.animate(self.straighten, duration/2, delay=duration/2)
def straighten(self):
'''rotate all dice to correct reading orientation'''
for d in self.dice:
d.straighten()
@ui.in_background #needed to use sleep, but could also just use animation delay arguments
def long_roll(self,sender=None, duration=0.5, num_rolls=10, straighten_after=True):
'''keep running the shuffle animation, before the last one completes.
duration: time if single roll, though it will be interupted a little way through
num_rolls: number of times to roll
'''
for i in range(num_rolls):
time.sleep(duration/5) #adjustable, but shorter than animation gives a more continuous shuffle appearance. todo: experiment with random times here
self.animated_shuffle(sender,duration, straighten_after= straighten_after and (i==num_rolls-1 ) )
if __name__=='__main__':
'''this stuff all belongs in a BoggleGame type class ... but just here for experiments'''
board_width=300
b=BoggleBoard(frame=(10,10,board_width,board_width),bg_color='#664040')
v=ui.View(frame=(0,0,board_width+20,board_width+64,))
btn1=ui.Button(frame=(10,board_width+20,200,44),title='Roll',bg_color='white')
btn1.border_color='blue'
btn1.width+=20
btn1.border_width=1
btn1.corner_radius=5
btn2=ui.Button(frame=(btn1.x+20+btn1.width,board_width+20,200,44),title='LongRoll',bg_color='white')
btn2.border_color='blue'
btn2.width+=20
btn2.border_width=1
btn2.corner_radius=5
btn1.flex='tr'
btn2.flex='tr'
v.add_subview(b)
v.add_subview(btn1)
v.add_subview(btn2)
'''some parameters to play with'''
straighten_dice=False #set False for more authentic feel
duration=.5 #set to 5 to see what is happening
btn1.action=partial(b.animated_shuffle, duration=duration, straighten_after=straighten_dice)
btn2.action=partial(b.long_roll, duration=duration, straighten_after=straighten_dice)
#v.present('') #auto size
v.present('sheet')
#v.layout()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment