" DCI program: BB3Greed "

" Exported from Squeak 7179-basic.76.image on: 18 December 2011 by: Trygve "

" Data perspective "

" Class BB3Cup "

" Cup rolling dice in a separate process.

See comment in Controller>>BB3Greed "

Morph subclass: #BB3Cup
    instanceVariableNames: 'isRolling dice rollingSemaphore'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BB3Greed-Data'

" BB3Cup instance methods in category: accessing "

rollHidden
    dice do:
            [:die |
            die rollHidden].
    ^self scoreHidden

scoreHidden
    | score faces noOfFaces theseDies triplets |
    noOfFaces := 0.
    dice do: [:die | noOfFaces := noOfFaces max: die noOfFaces].
    faces := Array new: noOfFaces.
    1 to: noOfFaces do: [:indx | faces at: indx put: OrderedCollection new].
    dice do: [:die | die isLocked ifFalse: [(faces at: die value) addLast: die]].
    score := 0.
    1 to: noOfFaces do:
        [:faceValue |
        theseDies := faces at: faceValue.
        triplets := theseDies size // 3.
        faceValue = 1
        ifTrue:
            [score := (triplets * 1000) +((theseDies size - (triplets * 3)) * 100) + score.
            "theseDies do: [:die | die isLocked: true]"]
        ifFalse:
            [faceValue = 5
            ifTrue:
                [score := (triplets * faceValue * 100) + ((theseDies size - (triplets * 3)) * 50) + score.
                "theseDies do: [:die | die isLocked: true]"]
            ifFalse:
                [score := (triplets * faceValue * 100) + score.
                "1 to: 3*triplets do: [:j | (theseDies at: j) isLocked: true]" ]]].
    ^score

unlockDice
    dice do: [:die | die isLocked: false].

" BB3Cup instance methods in category: private "

dice
    ^dice

initialize
    | die |
    super initialize.
    dice := OrderedCollection new.
"    rollingSemaphore := Semaphore forMutualExclusion."
    self
        layoutPolicy: TableLayout new;
        listDirection: #leftToRight;
        hResizing: #shrinkWrap;
        vResizing: #shrinkWrap;
        layoutInset: 10;
        cellInset: 5;
        borderWidth: 15;
        borderColor: Color blue;
        color: Color lightGreen.
     5 timesRepeat:
        [dice addFirst: (die := BB3Die new initialize).
        self addMorph: die.
        die stopStepping].
    self bounds: self fullBounds.

" Class BB3Die "

" See comment in Controller>>BB3Greed "

RectangleMorph subclass: #BB3Die
    instanceVariableNames: 'stringMorph noOfFaces value isLocked'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BB3Greed-Data'

" BB3Die instance methods in category: accessing "

isLocked
        ^isLocked

isLocked: bool
    (isLocked := bool)
        ifTrue: [self color: Color red]
        ifFalse: [self color: Color yellow].

noOfFaces
    ^noOfFaces

roll
    self isLocked
    ifFalse:
        [self value: (Collection randomForPicking next * noOfFaces + 0.49999) rounded].

rollHidden
    self isLocked
    ifFalse:
        [value := (Collection randomForPicking next * noOfFaces + 0.49999) rounded].

value
    ^value

value: int
    value := int.
    stringMorph contents: value printString.

" BB3Die instance methods in category: private "

initialize
    super initialize.
    noOfFaces := 6.
    isLocked := false.
    self
        layoutPolicy: TableLayout new;
        listDirection: #leftToRight;
        listCentering: #center;
        hResizing: #shrinkWrap;
        vResizing: #shrinkWrap;
        borderWidth: 5;
        borderColor: Color black;
        color: Color yellow.
    (stringMorph := StringMorph new)
        font: ((TextStyle named: 'BitstreamVeraSans') fontAt: 5).
    self addMorph: stringMorph.
    self roll.
    self bounds: self fullBounds.

" BB3Die class class methods in category: testing "

test1
    " BB3Die test1 "
    | dice results val |
    dice := BB3Die new initialize.
    results := (Array new: 6) atAllPut: 0.
    1000 timesRepeat:
        [dice step.
        val := dice value.
        results at: val put: (results at: val) + 1].
    results inspect

" Class BB3Player "

" See comment in Controller>>BB3Greed.

All players are instances of one of my subclasses. They differ as to playing strategy. "

TextMorph subclass: #BB3Player
    instanceVariableNames: 'name totalScore thisRoundScore'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BB3Greed-Data'

" BB3Player instance methods in category: accessing "

color
    ^self backgroundColor

color: aColor
    | w |
    self backgroundColor: aColor.
    (w := self world) ifNotNil: [w doOneCycle].

getName
    ^name

isEmptySeat
    ^name isNil

printOn: aStream
    super printOn: aStream.
    aStream nextPutAll: '(' , name printString , ')'.

setName: aStringOrNil
    name := aStringOrNil.
    self setContents.

thisRoundScore
    ^thisRoundScore

thisRoundScore: anInt
    thisRoundScore := anInt.
    self setContents.

totalScore
    ^totalScore

totalScore: anInt
    totalScore := anInt.
    self setContents.

wantsAnotherRoll: cup
    ^false

" BB3Player instance methods in category: private "

initialize
    super initialize.
    self
        textColor: Color green muchDarker;
        backgroundColor: Color lightGreen;
        borderColor: Color green;
        borderWidth: 5;
        layoutPolicy: (TableLayout new);
        hResizing: #spaceFill;
        vResizing: #spaceFill;
        beAllFont: ((TextStyle named: 'BitstreamVeraSans') fontAt: 2);
        autoFit: false.
    self setName: nil.

isLocked
    ^true

setContents
    | strm w |
    strm := TextStream on: (Text new: 50).
    self isEmptySeat
    ifFalse:
        [strm
            nextPutAll: name asText allBold;
            cr; nextPutAll: 'Total score = ' asText allItalics;
                nextPutAll: totalScore printString asText;
            cr; nextPutAll: 'Score this round = ' asText allItalics;
                nextPutAll: thisRoundScore printString asText].
    self
        newContents: strm contents;
        updateFromParagraph.
    (w := self world) ifNotNil: [w doOneCycle].

update: aParam
    self updateFromParagraph.

" Class BB3PlayerCareful "

" Rolls if there are three or more die to roll. "

BB3Player subclass: #BB3PlayerCareful
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BB3Greed-Data'

" BB3PlayerCareful instance methods in category: accessing "

wantsAnotherRoll: cup
    ^(cup dice select: [:die | die isLocked not]) size >= 5

" Class BB3PlayerOptimist "

" Rolls as long as there is a die to roll. "

BB3Player subclass: #BB3PlayerOptimist
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BB3Greed-Data'

" BB3PlayerOptimist instance methods in category: accessing "

wantsAnotherRoll: cup
    ^(cup dice select: [:die | die isLocked not]) size >= 2

" Class BB3PlayerSmart "

" Rolls experimentally a hundred times, makes an actual roll if the majority were beneficial. "

BB3Player subclass: #BB3PlayerSmart
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BB3Greed-Data'

" BB3PlayerSmart instance methods in category: accessing "

wantsAnotherRoll: cup
    | good burst score thisScore |
    good := 0.
    burst := 0.
    score := 0.
    100 timesRepeat: [(thisScore := cup rollHidden) > 0
            ifTrue: [good := good + 1. score := score + thisScore]
            ifFalse: [burst := burst + 1]].
    ^totalScore >= 300
        ifTrue:
            [(score / 100) > (totalScore * 0.3)] " Has entered game. "
        ifFalse:
            [false] " Entering game now. Take no chances. "
    
     "good > burst"

" Class BB3Seat "

" Room for a player.

See BB3Greed comment. "

Morph subclass: #BB3Seat
    instanceVariableNames: 'player'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BB3Greed-Data'

" BB3Seat instance methods in category: accessing "

player
    ^player

player: aPlayer
    player := aPlayer.
    self update.

" BB3Seat instance methods in category: event handling "

mouseDown: evt
    evt yellowButtonPressed
        ifTrue: ["First check for option (menu) click"
            ^ self yellowButtonActivity: evt shiftPressed].
    "self eventHandler
        ifNotNil: [self eventHandler mouseDown: evt fromMorph: self]"

" BB3Seat instance methods in category: private "

addPlayerClassName: clName
    | playerClass |
    playerClass := Smalltalk at: clName.
    player ifNotNil: [player delete].
    player := playerClass new initialize.
    player setName: playerClass name asString.
    self removeAllMorphs.
    self addMorph: player.

choosePlayer
    | playerClasses clIndx playerClass |
    playerClasses := BB3Player subclasses.
    clIndx := PopUpMenu
        withCaption: 'Please select player class'
        chooseFrom: (playerClasses collect: [:cl | cl name]).
    clIndx = 0 ifTrue: [^ self].
    playerClass := playerClasses at: clIndx.
    player ifNotNil: [player delete].
    player := playerClass new initialize.
    player setName: playerClass name asString.
    self removeAllMorphs.
    self addMorph: player.

handlesMouseDown: evt
    ^self innerBounds containsPoint: evt cursorPoint

initialize
    super initialize.
    self
        color: Color yellow;
        borderColor: Color black;
        borderWidth: 1;
        layoutPolicy: (TableLayout new);
        hResizing: #spaceFill;
        vResizing: #spaceFill.
    player := BB3Player subclasses first new initialize.
    player setName: player class name asString.
    self removeAllMorphs.
    self addMorph: player.

" BB3Seat instance methods in category: triggers "

delete
    " NOTE: Cannot use 'delete' method in Morph because it handles 'player' specially. STUPID! "
    | aWorld |
    self removeHalo.
    aWorld := self world ifNil: [World].
    aWorld ifNotNil:
     [self disableSubmorphFocusForHand: self activeHand.
     self activeHand releaseKeyboardFocus: self;
         releaseMouseFocus: self.].
    owner ifNotNil:
        [ self privateDelete.
        player ifNotNil: [ player delete]].

yellowButtonActivity: shiftState
    | aMenu |
    aMenu := MenuMorph new defaultTarget: self.
     aMenu
        add: 'choose player' action: #choosePlayer.
    aMenu popUpInWorld.

" Class BB3database "

" See BB3Greed comment. "

Object subclass: #BB3database
    instanceVariableNames: 'controller seats players cup'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BB3Greed-Data'

" BB3database instance methods in category: accessing "

controller
    ^controller

controller: ctr
    controller := ctr.

newPlayerOfClass: aPlayerClass
    | indx aPlayer |
    (indx := players findFirst: [:entry | entry isEmptySeat]) = 0
    ifTrue:
        [self inform: 'Maximum four players can play this game.\New player refused admittance.' withCRs.
        ^self].
    aPlayer := aPlayerClass new.
    aPlayer setName: aPlayerClass name asString.
    players at: indx put: aPlayer.
    self changed.
    ^aPlayer

players
    | res |
    res := OrderedCollection new.
    seats do: [:s | s player ifNotNil: [res add: s player]].
    ^res

seats
    ^seats

" BB3database instance methods in category: private "

initialize
    seats := Array new: 3.

" Context perspective "

" Context: BB3PlayGameCtx "

" Class BB3PlayGameCtx "

" Controls the playing process.

Otherwise see Controller>BB3Greed comment. "

BB1Context subclass: #BB3PlayGameCtx
    instanceVariableNames: 'data'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BB3Greed-Context'

" BB3PlayGameCtx instance methods in category: accessing "

data: aData
    data := aData.

enumerateCurrentPlayerDo: aBlock
    data players do:
        [:p |
        roleMap at: #CurrentPlayer put: p.
        aBlock value].

" BB3PlayGameCtx instance methods in category: role binding "

Cup
    ^data controller cup

CurrentContext
    ^self

CurrentPlayer
    ^data players first

Dice
    ^self Cup dice

GameMaster
    ^data controller

" BB3PlayGameCtx instance methods in category: triggers "

startPlayGreedOnData: dta
    data := dta.
    self runInteractionFromRoleNamed: #GameMaster.

" BB3PlayGameCtx class class methods in category: context diagram "

linkBreakPoints
    | dict |
    (dict := Dictionary new)
        yourself.
    ^dict.

rolePositions
    | dict |
    (dict := Dictionary new)
        at: #Dice put: 550@25;
        at: #CurrentPlayer put: 205@15;
        at: #CurrentContext put: 105@120;
        at: #Cup put: 430@20;
        at: #GameMaster put: 10@20;
        yourself.
    ^dict.

" BB3PlayGameCtx class class methods in category: role structure "

roleStructure
    ^super roleStructure
    at: #Dice put: #();
    at: #CurrentPlayer put: #(#Cup );
    at: #CurrentContext put: #();
    at: #Cup put: #(#Dice );
    at: #GameMaster put: #(#CurrentContext #CurrentPlayer );
        yourself.

No diagram

" Methodful Role Cup "

roll
    | w |
    20 timesRepeat:
        [Dice do:
            [:die |
            die roll].
            (w := self world) ifNotNil: [w doOneCycle].
            (Delay forMilliseconds: 100) wait.].
    ^Cup score

score
    | score faces noOfFaces theseDies triplets |
    noOfFaces := 0.
    self dice do: [:die | noOfFaces := noOfFaces max: die noOfFaces].
    faces := Array new: noOfFaces.
    " Faces is array. "
    " Index is a face value. "
    " Value is array of dice with this value. "
    1 to: noOfFaces do: [:indx | faces at: indx put: OrderedCollection new].
    self dice do: [:die | die isLocked ifFalse: [(faces at: die value) addLast: die]].
    " Compute score. "
    score := 0.
    1 to: noOfFaces do:
        [:faceValue |
        theseDies := faces at: faceValue.
        triplets := theseDies size // 3.
        faceValue = 1
        ifTrue:
            [score := (triplets * 1000) +((theseDies size - (triplets * 3)) * 100) + score.
            theseDies do: [:die | die isLocked: true]]
        ifFalse:
            [faceValue = 5
            ifTrue:
                [score := (triplets * faceValue * 100) + ((theseDies size - (triplets * 3)) * 50) + score.
                theseDies do: [:die | die isLocked: true]]
            ifFalse:
                [score := (triplets * faceValue * 100) + score.
                1 to: 3*triplets do: [:j | (theseDies at: j) isLocked: true]]]].
    ^score

" Methodless Role CurrentContext "

" Methodful Role CurrentPlayer "

newGame
    self color: Color lightGreen.
    self totalScore: 0.
    self thisRoundScore: 0.

yourTurn
    | oldColor rollScore |
    oldColor := self color.
    self color: Color lightOrange.
    self thisRoundScore: 0.
    Cup unlockDice.
    rollScore := Cup roll.
    (self totalScore = 0 and: [rollScore < 300])
    ifTrue:
        [" Not entered game. "
        rollScore := 0].
    [    self thisRoundScore: self thisRoundScore + rollScore.
        rollScore > 0 and: [self wantsAnotherRoll: Cup]]
    whileTrue:
        [rollScore := Cup roll].
    rollScore = 0
    ifTrue:
        [self thisRoundScore: 0. " Bust! "].
    self totalScore: self totalScore + self thisRoundScore.
"    self thisRoundScore: 0."
    (Delay forMilliseconds: 1000) wait.
    self color: oldColor.

" Methodless Role Dice "

" Methodful Role GameMaster "

run
    | lead |
    self newGame.
    " Initialize all players. "
    CurrentContext enumerateCurrentPlayerDo: [CurrentPlayer newGame].
    " Play rounds until game finished. "
    [    " Play one round. "
        CurrentContext enumerateCurrentPlayerDo: [CurrentPlayer yourTurn].
        lead := nil.
        CurrentContext enumerateCurrentPlayerDo:
            [(lead isNil or: [CurrentPlayer totalScore > lead totalScore])
                    ifTrue: [lead := CurrentPlayer]].
        self leader: lead.
        (lead notNil and: [lead totalScore < 3000 and: [self terminating not]])
    ] whileTrue.


" Controller perspective "

" Class BB3Greed "

" BB3Greed is a simple (and boring) game.
An unhappy DCI experiment.
Obscure Morphic code.
Obscure DCI code?

Player classes can be selected for selecting playing strategy. "

SystemWindow subclass: #BB3Greed
    instanceVariableNames: 'data greedMorph cup windowStrips greedTextMorph cupStrip text leader playButton terminating'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'BB3Greed-Controller'

" BB3Greed instance methods in category: accessing "

cup
    ^cup

leader
    ^leader

leader: lead
    | strm |
    leader := lead.
    strm := TextStream on: Text new.
    strm
        nextPutAll: 'Baby Greed. \ Game in progress' withCRs asText allBold;
        cr; nextPutAll: 'The leader is: ' asText , leader getName asText allItalics;
        cr; nextPutAll: ('with score: ' , leader totalScore asString) asText.
    greedMorph contents: strm contents..

newGame
    self color: Color lightBlue.
    self borderColor: Color black.
    leader := nil.
    greedMorph contents: 'Baby Greed. \ Game starting' withCRs asText allBold.

terminate
    terminating := true.

terminating
    ^terminating

updateText
    | txt strm |
    leader
    ifNil:
        [txt := 'Baby Greed. \ Game in progress' withCRs asText allBold]
    ifNotNil:
        [leader totalScore = 0
        ifTrue:
            [txt := 'Baby Greed. \ Game in progress' withCRs asText allBold]
        ifFalse:
            [leader totalScore < 3000
            ifTrue:
                [strm := TextStream on: Text new.
                strm
                    nextPutAll: 'Baby Greed. \ Game in progress' withCRs asText allBold;
                    cr; nextPutAll: 'The leader is: ' asText , leader getName asText allItalics;
                    cr; nextPutAll: ('with score: ' , leader totalScore asString) asText.
                txt := strm contents]
            ifFalse:
                [strm := TextStream on: Text new.
                strm
                    nextPutAll: 'Baby Greed finished' asText allBold;
                    cr; nextPutAll: 'The winner is: ' asText , leader getName asText allItalics;
                    cr; nextPutAll: (' with score: ' , leader totalScore asString) asText allItalics.
                txt := strm contents]
        ]].
    greedMorph contents: txt.

" BB3Greed instance methods in category: open/close "

delete
    self terminate.
    super delete.

" BB3Greed instance methods in category: overrides "

activate
    "Bring me to the front and make me able to respond to mouse and keyboard"

    | oldTop outerMorph sketchEditor pal |
    outerMorph _ self topRendererOrSelf.
    outerMorph owner ifNil: [^ self "avoid spurious activate when drop in trash"].
    oldTop _ TopWindow.
    TopWindow _ self.
    oldTop ifNotNil: [oldTop passivate].
    outerMorph owner firstSubmorph == outerMorph
        ifFalse: ["Bring me (with any flex) to the top if not already"
                outerMorph owner addMorphFront: outerMorph].
    self submorphsDo: [:m | m unlock].
    labelArea ifNotNil:
        [labelArea submorphsDo: [:m | m unlock].
        self setStripeColorsFrom: self color. "self paneColorToUse"].
    self isCollapsed ifFalse:
        [model modelWakeUpIn: self.
        self positionSubmorphs.
        labelArea ifNil: [self adjustBorderUponActivationWhenLabeless]].

    (sketchEditor _ self extantSketchEditor) ifNotNil:
        [sketchEditor comeToFront.
        (pal _ self world findA: PaintBoxMorph) ifNotNil:
            [pal comeToFront]].

addMorph: aMorph fullFrame: aLayoutFrame

    | left right bottom top windowBorderWidth |
    windowBorderWidth _ self class borderWidth.

    left _ aLayoutFrame leftOffset ifNil: [0].
    right _ aLayoutFrame rightOffset ifNil: [0].

    bottom _ aLayoutFrame bottomOffset ifNil: [0].
    top _ aLayoutFrame topOffset ifNil: [0].
    
    aLayoutFrame rightFraction = 1 ifTrue: [aLayoutFrame rightOffset: right - windowBorderWidth].
    aLayoutFrame leftFraction = 0
        ifTrue: [aLayoutFrame leftOffset: left + windowBorderWidth]
        ifFalse: [aLayoutFrame leftOffset: left + ProportionalSplitterMorph splitterWidth].

    aLayoutFrame bottomFraction = 1 ifTrue: [aLayoutFrame bottomOffset: bottom - windowBorderWidth].
    aLayoutFrame topFraction = 0
        ifTrue: [aLayoutFrame topOffset: top]
        ifFalse: [aLayoutFrame topOffset: top + ProportionalSplitterMorph splitterWidth].
    
    (aMorph class name = #BrowserCommentTextMorph) ifTrue:
        [aLayoutFrame rightOffset: windowBorderWidth negated.
        aLayoutFrame leftOffset: windowBorderWidth.
        aLayoutFrame bottomOffset: windowBorderWidth negated.
        aLayoutFrame topOffset: (windowBorderWidth negated) + 4].
    
"    super addMorph: aMorph fullFrame: aLayoutFrame."
    aMorph layoutFrame: aLayoutFrame.
    aMorph hResizing: #spaceFill; vResizing: #spaceFill.
    self addMorph: aMorph.

    paneMorphs _ paneMorphs copyReplaceFrom: 1 to: 0 with: (Array with: aMorph).
"    aMorph adoptPaneColor: self paneColor."
"    aMorph borderWidth: 1; borderColor: Color lightGray; color: Color white."
    Preferences scrollBarsOnRight    "reorder panes so flop-out right-side scrollbar is visible"
        ifTrue: [self addMorphBack: aMorph].
        
"    self addPaneSplitters"

color: aColor
"    self traceRM: aColor levels: 2."
    ^super color: aColor

fillStyle: aFillStyle
    "self traceRM: aFillStyle asColor levels: 4."
    ^super fillStyle: aFillStyle

openInWorld: aWorld
    self bounds: (Rectangle originFromUser: self extent).
    ^self openAsIsIn: aWorld

passivate
    "Make me unable to respond to mouse and keyboard"

    self setStripeColorsFrom: self color. "self paneColorToUse."
    model modelSleep.

    "Control boxes remain active, except in novice mode"
    self submorphsDo: [:m |
        m == labelArea ifFalse:
            [m lock]].
    labelArea ifNotNil:
        [labelArea submorphsDo: [:m |
            (m == closeBox or: [m == collapseBox])
                ifTrue:
                    [Preferences noviceMode ifTrue: [m lock]]
                ifFalse:
                    [m lock]]]
        ifNil: "i.e. label area is nil, so we're titleless"
            [self adjustBorderUponDeactivationWhenLabeless].
    self world ifNotNil: "clean damage now, so dont merge this rect with new top window"
        [self world == World ifTrue: [self world displayWorld]].

privateColor: aColor
"    self traceRM: aColor levels: 2."
    ^super privateColor: aColor

" BB3Greed instance methods in category: private "

buildCupStrip
    | strip |
    strip := AlignmentMorph newRow
            layoutPolicy: TableLayout new;
            listDirection: #leftToRight;
            hResizing: #spaceFill;
            vResizing: #spaceFill;
            cellInset: 1;
            borderWidth: 0.    
    (playButton := (StringButtonMorph
                        contents: 'Play'
                        font: ( ((TextStyle named: 'BitstreamVeraSans') fontAt: 5))))
            color: Color black;
            borderWidth: 5;
            borderColor: Color red;
            target: self;
            arguments: Array new;
            actionSelector: #startPlayGreed;
            actWhen: #buttonDown.
    (greedMorph := TextMorph new)
        layoutPolicy: TableLayout new;
        hResizing: #spaceFill;
        vResizing: #spaceFill;
        textColor: Color blue;
        backgroundColor: Color lightBlue;
        borderColor: Color gray;
        borderWidth: 1;
        beAllFont: ((TextStyle named: 'BitstreamVeraSans') fontAt: 2);
        contents: text;
        autoFit: false.
    cup := BB3Cup new initialize.
    strip
        addMorph: cup;
        addMorph: playButton;
        addMorph: greedMorph;
        bounds: strip fullBounds;
        color: Color transparent.
    ^strip

data
    ^self

initialize
    super initialize.
    self extent: 600@550.
    self borderWidth: 3.
    self color: Color lightBlue.
    self borderColor: Color black.
    text := 'Welcome to the game of Greed.\Play it.' withCRs asText allBold.
    leader := nil.
    data := (BB3database new initialize) controller: self; yourself.
    terminating := false.

open
    " BB3Greed open. "
    | relY cupBottom aSeatMorph left right deltaY |
    self setLabel: 'BabyGreed'.
    windowStrips := OrderedCollection new.
    left := 0.01.
    right := 0.99.
    "------"
    cupStrip := self buildCupStrip.
    cupBottom := cupStrip bottom.
    self
        addMorph: cupStrip
        fullFrame: (LayoutFrame fractions: (left@0 corner: right@0) offsets: (0@0 corner: 0@cupBottom)).
    "------"        
    relY := 0.
    deltaY := 1/(data seats size+1).
    1 to: 3 do:
        [:indx |
        aSeatMorph := BB3Seat new.
        data seats at: indx put: aSeatMorph.
        self
            addMorph: aSeatMorph
            fullFrame:
                (LayoutFrame
                    fractions: (left@relY corner: right@(relY := relY+deltaY))
                    offsets: (0@cupBottom corner: 0@cupBottom)).
            aSeatMorph addPlayerClassName: (#(BB3PlayerCareful BB3PlayerOptimist BB3PlayerSmart) at: indx).
        ].
    self openInWorld.

" BB3Greed instance methods in category: triggers "

startPlayGreed
    | strm |
    BB3PlayGameCtx new startPlayGreedOnData: data.
    strm := TextStream on: Text new.
    strm
        nextPutAll: 'Baby Greed finished' asText allBold;
        cr; nextPutAll: 'The winner is: ' asText , leader getName asText allItalics;
        cr; nextPutAll: (' with score: ' , leader totalScore asString) asText allItalics.
    greedMorph contents: strm contents.

" BB3Greed class class methods in category: class initialization "

initialize
    " BB3Greed initialize "
    TheWorldMenu
        registerOpenCommand:
            {'BB3Greed game'. {BB3Greed. #open}. 'A game program exemplifying the use of BabyIDE'}.

" BB3Greed class class methods in category: instance creation "

open
    " BB3Greed open. "
    ^(self basicNew initialize) open