I'm still at The Server Side Java Symposium in Las Vegas, and I think that walking by all these slot machines has inspired this blog post. Today's example builds on the program in the Roll the Dice post in order to create a Yahtzee dice roller and scorer. Each time you click the Roll button, the five dice assume random values, and the combination of values is scored according to the possible categories in the bottom portion of a Yahtzee scoring sheet. Here's a screenshot:
In addition to the Dice.fx and PipPlacement.fx files from the Roll the Dice post, this program consists of the following source code files.
YahtzeeMain.fx
/*
* YahtzeeMain.fx -
* A compiled JavaFX program that demonstrates creating custom
* components with CompositeNode and evaluates Yahtzee dice rolls.
*
*
* Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
* to serve as a JavaFX Script example.
*/
import javafx.ui.*;
import javafx.ui.canvas.*;
import java.lang.System;
Frame {
var model = YahtzeeModel {}
width: 510
height: 400
title: "Roll Dice and Evaluate Yahtzee Combinations"
background: Color.WHITE
content:
BorderPanel {
center:
Box {
var evalFont =
Font {
size: 20
}
orientation: Orientation.VERTICAL
content: [
Canvas {
content:
for (diceNum in [0 .. model.numDice - 1]) {
model.newDice =
Dice {
x: diceNum * 100 + 10
y: 10
width: 80
height: 80
faceColor: Color.RED
pipColor: Color.WHITE
}
}
},
GroupPanel {
var fiveOfKindRow = Row { alignment: Alignment.BASELINE }
var largeStraightRow = Row { alignment: Alignment.BASELINE }
var smallStraightRow = Row { alignment: Alignment.BASELINE }
var fullHouseRow = Row { alignment: Alignment.BASELINE }
var fourOfKindRow = Row { alignment: Alignment.BASELINE }
var threeOfKindRow = Row { alignment: Alignment.BASELINE }
var chanceRow = Row { alignment: Alignment.BASELINE }
var labelsColumn = Column {
alignment: Alignment.TRAILING
}
var fieldsColumn = Column {
alignment: Alignment.LEADING
}
rows: [
fiveOfKindRow,
largeStraightRow,
smallStraightRow,
fullHouseRow,
fourOfKindRow,
threeOfKindRow,
chanceRow
]
columns: [
labelsColumn,
fieldsColumn
]
content: [
SimpleLabel {
font: evalFont
row: fiveOfKindRow
column: labelsColumn
text: "Five of a Kind (Yahtzee):"
},
SimpleLabel {
font: evalFont
row: fiveOfKindRow
column: fieldsColumn
text: bind
if (model.fiveOfKind)
"{model.fiveOfKindScore}"
else "N/A"
},
SimpleLabel {
font: evalFont
row: largeStraightRow
column: labelsColumn
text: "Large Straight:"
},
SimpleLabel {
font: evalFont
row: largeStraightRow
column: fieldsColumn
text: bind
if (model.largeStraight)
"{model.largeStraightScore}"
else "N/A"
},
SimpleLabel {
font: evalFont
row: smallStraightRow
column: labelsColumn
text: "Small Straight:"
},
SimpleLabel {
font: evalFont
row: smallStraightRow
column: fieldsColumn
text: bind
if (model.smallStraight)
"{model.smallStraightScore}"
else "N/A"
},
SimpleLabel {
font: evalFont
row: fullHouseRow
column: labelsColumn
text: "Full House:"
},
SimpleLabel {
font: evalFont
row: fullHouseRow
column: fieldsColumn
text: bind
if (model.fullHouse)
"{model.fullHouseScore}"
else "N/A"
},
SimpleLabel {
font: evalFont
row: fourOfKindRow
column: labelsColumn
text: "Four of a Kind:"
},
SimpleLabel {
font: evalFont
row: fourOfKindRow
column: fieldsColumn
text: bind
if (model.fourOfKind)
"{model.sumOfDiceValues}"
else "N/A"
},
SimpleLabel {
font: evalFont
row: threeOfKindRow
column: labelsColumn
text: "Three of a Kind:"
},
SimpleLabel {
font: evalFont
row: threeOfKindRow
column: fieldsColumn
text: bind
if (model.threeOfKind)
"{model.sumOfDiceValues}"
else "N/A"
},
SimpleLabel {
font: evalFont
row: chanceRow
column: labelsColumn
text: "Chance:"
},
SimpleLabel {
font: evalFont
row: chanceRow
column: fieldsColumn
text: bind "{model.sumOfDiceValues}"
},
]
},
]
}
bottom:
FlowPanel {
content:
Button {
text: "Roll"
defaultButton: true
action:
function():Void {
model.roll();
}
}
}
}
visible: true
onClose:
function():Void {
System.exit(0);
}
}
YahtzeeModel.fx
/*
* YahtzeeModel.fx -
* The model behind the Yahtzee dice roll and combination evaluation
*
* Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
* to serve as a JavaFX Script example.
*/
import javafx.lang.Sequences;
import java.lang.System;
class YahtzeeModel {
attribute numDice:Integer = 5;
attribute diceDistribution:Integer[];
attribute newDice:Dice on replace {
insert newDice into dice;
}
attribute dice:Dice[];
attribute fiveOfKind:Boolean;
attribute largeStraight:Boolean;
attribute smallStraight:Boolean;
attribute fullHouse:Boolean;
attribute fourOfKind:Boolean;
attribute threeOfKind:Boolean;
attribute fiveOfKindScore:Integer = 50;
attribute largeStraightScore:Integer = 40;
attribute smallStraightScore:Integer = 30;
attribute fullHouseScore:Integer = 25;
attribute sumOfDiceValues:Integer = 0;
function roll():Void {
for (die in dice) {
die.roll();
}
evalYahtzeeCombos();
}
function evalYahtzeeCombos() {
var values =
for (val in dice) {
val.value;
}
var maxVal:Integer = Sequences.max(values, null) as Integer;
var minVal:Integer = Sequences.min(values, null) as Integer;
// Create a sequence that contains the distribution of values
// and Calclulate the sum of the dice values
diceDistribution =
for (i in [1 .. 6]) 0;
sumOfDiceValues = 0;
for (val in values) {
diceDistribution[val - 1]++;
sumOfDiceValues += val;
}
// Determine if five-of-a-kind
fiveOfKind =
((for (occurance in diceDistribution
where occurance >= 5) occurance) <> []);
// Determine if four-of-a-kind
fourOfKind =
((for (occurance in diceDistribution
where occurance >= 4) occurance) <> []);
// Determine if three-of-a-kind
threeOfKind =
sizeof (for (occurance in diceDistribution
where occurance >= 3) occurance) > 0;
// Determine if full house
fullHouse =
sizeof (for (occurance in diceDistribution
where occurance == 3) occurance) > 0 and
sizeof (for (occurance in diceDistribution
where occurance == 2) occurance) > 0;
// Determine if large straight
largeStraight =
sizeof (for (occurance in diceDistribution
where occurance > 1) occurance) == 0 and
(maxVal - minVal == 4);
// Determine if small straight
smallStraight =
sizeof (for (occurance in diceDistribution
where occurance == 2) occurance) == 1 and
(maxVal - minVal == 3);
}
}
A JavaFX Script concept that we haven't covered yet is the set of functions available in the new javafx.lang.Sequences package. These functions enable you to perform operations (such as sorting) on a sequence. In this program I'm using the max and min functions of that class to find the largest and smallest value in a dice roll.
Enjoy!
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications
Immediate eBook (PDF) download available at the book's Apress site
Great Example, Jim. I just used it to test in the NetBeans IDE 6.1. I noticed that the Preview / FXPad mode is unavailable from the IDE. Is this mode no longer available?
Posted by: Peter Pilgrim | March 29, 2008 at 12:12 PM
I agree that this is a trivial example. The purpose of many of the examples in this weblog are meant to teach concepts. Please see the Freebase Browser post for an example (albeit in interpreted JavaFX Script) of a more fully featured application, that communicates using JSON protocol with a server:
http://learnjavafx.typepad.com/weblog/2007/10/spotting-javafx.html
Also, here is a simpler example of talking to the same server (freebase.com) that shows the compiled JavaFX Script code:
http://learnjavafx.typepad.com/weblog/2008/02/compiled-javafx.html
Posted by: Jim Weaver | March 28, 2008 at 01:53 PM
As a developer, if I'm going to use JavaFX and recommend to my clients, it better do something substantially better than what people normally do with plain old HTML + JavaScript.
This kind of thing is pretty trivial do do with JavaScript or alm ost any GUI toolset.
Posted by: javadev | March 27, 2008 at 05:10 PM