slides

advertisement
What is Ligretto?
A case study in compositional and functional programming
Peter Achten, Jurriën Stutterheim, László Domoszlai, Rinus Plasmeijer
P.Achten@cs.ru.nl, j.stutterheim@cs.ru.nl, dlacko@gmail.com, rinus@cs.ru.nl
Radboud University Nijmegen, Netherlands, ICIS, MBSD
What is Ligretto?
•  Card game for 2-12 players
•  Simultaneous, not turn-based
•  Player cards get moved to middle
•  Player cards get moved per player
•  It's hectic and fun!
Features:
•  Application logic
•  Interaction
•  Coordination
FP
TOP
iTask
FP day january 9 2015 / TU Twente
2
iTask = FP + Interaction + Coordination
•  TOP paradigm for developing distributed multi-user applications
•  Abstraction of work performed by computers and humans
•  Tasks are observable and first-class citizens
FP
TOP
FP day january 9 2015 / TU Twente
3
iTask = FP + Interaction + Coordination
•  Typed abstraction: (Task m)
•  Coordination via combinator functions (step (>>*), parallel, shared data sources, ...)
•  Interaction via pure functions and editors
FP
viewSharedInformation
viewInformation
:: Title [ViewOption
:: Title [ViewOption
r
r
] (RWShared r w) -> Task r | iTask r
] r
-> Task r | iTask r
updateInformation
:: Title [UpdateOption r r] r
-> Task r | iTask r
updateSharedInformation :: Title [UpdateOption r w] (RWShared r w) -> Task w | iTask r
& iTask w
TOP
•  Compositional
•  Implementation tool chain:
•  editlets, JavaScript, HTML, ...
•  Works in any browser
FP day january 9 2015 / TU Twente
4
Separation of concerns, part u
:: Card
= { back :: Color
, front :: Color
, no
:: Int }
:: Color = Red | Green
| Blue | Yellow
FP
C
model
...
updateSharedInformation name [] game
>>* [OnValue player_wins]
...
D
FP day january 9 2015 / TU Twente
C
TOP
coordination
5
zed
i
m
o
cust
iTask = FP + Interaction + Coordination
imageView :: (m -> Image m) -> ViewOption m | iTask m
FP
viewSharedInformation
viewInformation
:: Title [ViewOption
:: Title [ViewOption
r
r
] (RWShared r w) -> Task r | iTask r
] r
-> Task r | iTask r
updateInformation
:: Title [UpdateOption r r] r
-> Task r | iTask r
updateSharedInformation :: Title [UpdateOption r w] (RWShared r w) -> Task w | iTask r
& iTask w
TOP
imageUpdate :: (r -> m) (m -> Image m) (r m -> w) -> UpdateOption r w | iTask m
FP day january 9 2015 / TU Twente
6
zed
i
m
o
cust
iTask = FP + Interaction + Coordination
•  Typed abstraction: (Image m)
•  Interaction via pure functions and editors
FP
viewSharedInformation
viewInformation
:: Title [ViewOption
:: Title [ViewOption
r
r
] (RWShared r w) -> Task r | iTask r
] r
-> Task r | iTask r
updateInformation
:: Title [UpdateOption r r] r
-> Task r | iTask r
updateSharedInformation :: Title [UpdateOption r w] (RWShared r w) -> Task w | iTask r
& iTask w
•  Compositional
•  Implementation tool chain:
•  editlets, JavaScript, HTML, SVG
•  Works in any browser
FP day january 9 2015 / TU Twente
• 
• 
• 
• 
• 
TOP
Scalable by design
XML-based standard by W3C
Supported by major browsers
Declarative by nature (mostly)
Excellent hit detection
7
Separation of concerns, part v
:: Card = { back
, front
, no
:: Color = Red |
| Blue |
FP
model
:: Color
:: Color
:: Int }
Green
Yellow
C
...
updateSharedInformation
name
[imageUpdate ...]
game
>>* [OnValue player_wins]
...
Graphics.Scalable
TOP
custom
coordination
interaction
FP day january 9 2015 / TU Twente
8
A compositional Ligretto specification
Model
•  entities, relations
•  rules of the game
Custom Interaction
•  cards, layout, interaction
FP day january 9 2015 / TU Twente
Coordination
•  overall game structure
9
Model
•  entities, relations
•  rules of the game
:: Card
::
::
::
::
::
::
= { back
::
, front
::
, no
::
Color
= Red | Green |
Pile
:== [Card]
Player
= { color
::
, row
::
, ligretto ::
, hand
::
, seed
::
Hand
= { conceal ::
, discard ::
GameSt
= { middle
::
, players ::
NoOfPlayers :== Int
play_concealed_pile
play_hand_card
play_row_card
get_player
colors
initial_player
FP day january 9 2015 / TU Twente
::
::
::
::
::
::
Color
Color
Int }
Blue | Yellow
Color
[Card]
Pile
Hand
Int }
Pile
Pile }
[Pile]
[Player] }
Color
GameSt
Color
GameSt
Color Int GameSt
Color
GameSt
NoOfPlayers
NoOfPlayers Color Int
->
->
->
->
->
->
GameSt
GameSt
GameSt
Player
[Color]
Player
10
Coordination
•  overall game structure
play_Ligretto
play_Ligretto
=
>>= \me ->
>>= \you ->
>>= \rs
:: Task (Color,User)
get currentUser
invite_friends
let us = zip2 (colors (1+length you)) [me:you]
in allTasks (repeatn (length us)
(get randomInt))
-> let game = { middle = repeatn 16 []
, players = [ initial_player
(length us) c (abs r)
\\ (c,_) <- us & r <- rs]
}
in withShared game (play_game us)
invite_friends :: Task [User]
invite_friends
=
enterSharedMultipleChoice
"Select friends to play with" [] users
>>= \you -> if (not (isMember (length you) [1..3]))
(
viewInformation "Oops" []
"number of friends must be 1, 2, or 3"
>>| invite_friends
) (return you)
FP day january 9 2015 / TU Twente
11
Coordination
•  overall game structure
play_game :: [(Color,User)] (Shared GameSt)
-> Task (Color,User)
play_game us game_st
=
anyTask
[u @: play
(c,u) game_st \\ (c,u) <- us]
>>= \w -> allTasks
[u @: accolades w (c,u) game_st \\ (c,u) <- us]
>>| return w
play :: (Color,User) (Shared GameSt) -> Task (Color,User)
play (c,u) game_st
=
updateSharedInformation (toString u)
[imageViewUpdate id (player_perspective c)
(\_ st -> st)] game_st
>>* [OnValue player_wins]
where
player_wins (Value game _)
| isEmpty (get_player c game).ligretto = Just (return (c,u))
player_wins _
= Nothing
accolades :: (Color,User) (Color,User) (Shared GameSt)
-> Task GameSt
accolades (_,winner) (c,_) game_st
= viewSharedInformation ("The winner is " <+++ winner)
[imageView (player_perspective c)] game_st
FP day january 9 2015 / TU Twente
12
Custom Interaction
•  cards, layout, interaction
•  Compositional images:
•  which are the image patterns?
•  which are the layout patterns?
•  which are the image layers?
•  Interactive images:
•  which (composite) images are interactive?
FP day january 9 2015 / TU Twente
13
Graphics.Scalable concepts, part u
•  Every value of type (Image m):
• 
• 
• 
• 
is infinitely wide and perfectly transparent
has a local coordinate system defined by the span box
measures are Span values (px :: Real -> Span)
can be subject to transformation:
scaling, skewing, rotating, flipping, and masking
•  Basic images: the usual suspects
•  empty, text, circle, ellipse, rect, polygon, polyline, xline, yline, line
•  Image attributes
•  attributes alter appearance but not span box
same as SVG
name-attribute pairs
class <@< attr :: (Image m) (attr m) -> Image m
FP day january 9 2015 / TU Twente
14
Custom Interaction
•  cards, layout, interaction
card_width
card_height
no_stroke_color
no_stroke_color
no_stroke_color
no_stroke_color
= px 58.5
= px 90.0
Red
Green
Blue
Yellow
=
=
=
=
Blue
Red
Green
Green
instance toSVGColor Color where
toSVGColor Red
= toSVGColor
toSVGColor Green = toSVGColor
toSVGColor Blue
= toSVGColor
toSVGColor Yellow = toSVGColor
"darkred"
"darkgreen"
"midnightblue"
"gold"
card_shape
= rect card_width card_height
<@< {xradius = card_height /. 18}
<@< {yradius = card_height /. 18}
cardfont size
= normalFontDef "Verdana" size
big_no no color = text (cardfont 20.0) (toString no)
<@< {fill
= toSVGColor "white"}
<@< {stroke = toSVGColor color }
ligretto color = text (cardfont 12.0) "Ligretto"
<@< {fill
= toSVGColor "none" }
<@< {stroke = toSVGColor color }
FP day january 9 2015 / TU Twente
15
Graphics.Scalable concepts, part v
•  Image composition is just stacking (z-axis) and aligning (x-, y-axis)
:: Layout m
:== [ImageOffset] [Image m] (Host m) -> Image m
:: Host
m
:== Maybe (Image m)
:: ImageOffset :== (Span, Span)
collage :: Layout m
•  Derived image composition functions:
overlay, beside, above, grid, margin
FP day january 9 2015 / TU Twente
16
Custom Interaction
•  cards, layout, interaction
card_image :: SideUp Card -> Image m
card_image side card
| side === Front
= let no = margin (px 5.0)
(big_no card.no
(no_stroke_color card.front))
in overlay [(AtMiddleX,AtTop),(AtMiddleX,AtBottom)] []
[no, rotate (deg 180.0) no] host
| otherwise
= overlay [(AtLeft,AtBottom)] []
[skewy (deg -20.0) (ligretto card.back)] host
where
host = Just (card_shape
<@< {fill = if (side === Front)
(toSVGColor card.front)
(toSVGColor "white")})
card = {back = Red, front = Green, no = 7}
FP day january 9 2015 / TU Twente
17
Custom Interaction
•  cards, layout, interaction
no_card_image :: Image m
no_card_image
= overlay [(AtMiddleX,AtMiddleY)] []
[text (cardfont 12.0) "empty"]
(Just (card_shape
<@< {fill = toSVGColor "lightgrey"}))
pile_of_cards :: SideUp Pile -> Image m
pile_of_cards side pile
= overlay [] [(zero,card_height /. 18 *. dy) \\ dy <- [0..]]
(map (card_image side) (reverse pile)) host
where
host = Just no_card_image
pile_image :: SideUp Pile -> Image m
pile_image side pile
| no_of_cards > 10
= above [AtMiddleX] []
[text (cardfont 10.0) (toString no_of_cards)
,top_cards_image] Nothing
| otherwise = top_cards_image
where
no_of_cards
= length pile
top_cards_image = pile_of_cards side (take 10 pile)
FP day january 9 2015 / TU Twente
18
Custom Interaction
•  cards, layout, interaction
middle_image :: [Pile] -> Image m
middle_image middle
= circular (card_height *. 2) (2.0 * pi)
(map (pile_image Front) middle))
circular :: Span Real [Image m] -> Image m
circular r a imgs
= overlay (repeat (AtMiddleX,AtMiddleY))
[ (~r *. cos angle, ~r *. sin angle)
\\ i
<- [0.0, sign_a ..]
, angle <- [i * alpha - pi / 2.0]
]
[ rotate (rad (i * alpha)) img
\\ i
<- [0.0, sign_a ..]
& img
<- imgs
]
(Just (empty (r *. 2) (r *. 2)))
where
sign_a = toReal (sign a)
alpha = (toRad (normalize (rad a)))/(toReal (length imgs))
FP day january 9 2015 / TU Twente
19
Graphics.Scalable concepts, part w
•  Any (compositional) image is made interactive with the on-click attribute:
:: OnClickAttr m = { onclick :: (m -> m) }
instance <@< OnClickAttr
tuneIf :: Bool (Image m) (attr m) -> Image m | <@< attr
tuneIf yes img attr = if yes (img <@< attr) img
FP day january 9 2015 / TU Twente
20
Custom Interaction
•  cards, layout, interaction
row_images :: Bool [Card] -> [Image GameSt]
row_images interactive row
= [ tuneIf interactive (card_image Front row_card)
{onclick = play_row_card row_card.back no}
\\ row_card <- row & no <- [1..]
]
hand_images :: Bool Hand Color -> [Image GameSt]
hand_images interactive {conceal,discard} c
= [ tuneIf interactive (pile_image Back conceal)
{onclick = play_concealed_pile c}, space
, tuneIf interactive (pile_image Front discard)
{onclick = play_hand_card c}
]
space = empty (card_width /. 4) zero
player_image :: Bool Span Player -> Image GameSt
player_image interactive r player
= circular r (0.4*pi)
( row_images interactive player.row
++ [space, pile_image Front player.ligretto, space]
++ hand_images interactive player.hand player.color
)
FP day january 9 2015 / TU Twente
21
Custom Interaction
•  cards, layout, interaction
game_image :: Color GameSt -> Image GameSt
game_image color game
= overlay (repeat (AtMiddleX,AtMiddleY)) []
[ rotate (rad (i * angle - 0.25 * pi)) img
\\ img <- [ player_image (player.color === color)
r player
\\ player <- game.players
]
& i
<- [0.0, 1.0..]
]
(Just (middle_image game.middle))
where
r
= card_height *. 4
angle = 2.0 * pi / (toReal (length game.players))
player_perspective :: Color GameSt -> Image GameSt
player_perspective color game
= rotate (rad (0.05 * pi - toReal my_no * angle))
(game_image color game)
where
my_no = hd [i \\ player <- game.players
& i
<- [0 .. ]
| player.color === color
]
angle = 2.0 * pi / (toReal (length game.players))
FP day january 9 2015 / TU Twente
22
Graphics.Scalable to SVG challenges
•  SVG transformations vs Graphics.Scalable transformations
•  SVG maintains a Current Transformation Matrix (CTM) for each image’s axes,
not the images
•  Every transformation alters the CTM, therefore the axes
•  SVG users need to think about the order in which transformations are applied
•  Graphics.Scalable transforms the images, not the axes
•  Text widths can only be calculated on the client
•  Width crucial for layout combinators
•  Round-trip to client required
•  SVG API is lacking regarding font metrics
•  ascent, descent, and x-height
FP day january 9 2015 / TU Twente
23
Current and future work
•  More performance improvements
•  Complete the event model (currently limited to on-click)
•  Tonic: An Infrastructure to Graphically Represent the Definition and
Behaviour of Tasks1 (TFP'14)
•  Extend Graphics.Scalable layout formalism to include task layout
1
http://link.springer.com/chapter/10.1007/978-3-319-14675-1_8
FP day january 9 2015 / TU Twente
24
Download