Created
April 12, 2018 09:23
-
-
Save jsbain/3dbd60b05e13fa32a497122ff9754d7b to your computer and use it in GitHub Desktop.
boggle.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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