├── .gitignore
├── day1
├── session1
│ ├── README
│ ├── project
│ │ ├── build.properties
│ │ ├── build
│ │ │ └── Project.scala
│ │ └── plugins
│ │ │ └── Plugins.scala
│ ├── scala-workshop.iml
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── org
│ │ ├── datamodeling
│ │ ├── Example10_Lazy.scala
│ │ ├── Example11_Traits.scala
│ │ ├── Example12_Inheritance.scala
│ │ ├── Example13_CaseClasses.scala
│ │ ├── Example14_Types.scala
│ │ ├── Example1_Values.scala
│ │ ├── Example2_Evolve.scala
│ │ ├── Example3_OurFirstMonster.scala
│ │ ├── Example4_MultipleMonsters.scala
│ │ ├── Example5_ClassesVsInstances.scala
│ │ ├── Example6_NamedAndDefault.scala
│ │ ├── Example7_Specializing.scala
│ │ ├── Example8_Abstract.scala
│ │ ├── Example9_Alignment.scala
│ │ └── TestPrivate.scala
│ │ ├── fdgame
│ │ └── solutions
│ │ │ ├── Armor.scala
│ │ │ ├── Character.scala
│ │ │ ├── Container.scala
│ │ │ ├── Item.scala
│ │ │ ├── Key.scala
│ │ │ ├── Potion.scala
│ │ │ └── Weapon.scala
│ │ ├── frontierdevelopers
│ │ └── scalaworkshop
│ │ │ ├── ConsoleGame.scala
│ │ │ ├── NullAndOption.scala
│ │ │ └── PuttingItAllTogether.scala
│ │ └── testgame
│ │ └── SwordWarrior.scala
├── session2
│ ├── README
│ ├── pom.xml
│ ├── project
│ │ ├── build.properties
│ │ ├── build
│ │ │ └── Project.scala
│ │ └── plugins
│ │ │ └── Plugins.scala
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── Functions.scala
└── session4
│ └── README
├── day2
├── session1
│ └── README
├── session2
│ ├── README
│ ├── data
│ │ ├── akka.conf
│ │ └── logback.xml
│ ├── project
│ │ ├── build.properties
│ │ └── build
│ │ │ └── ConcurrencyProject.scala
│ └── src
│ │ └── main
│ │ ├── resources
│ │ └── logback.xml
│ │ └── scala
│ │ └── org
│ │ └── frontierdevelopers
│ │ └── scalaworkshop
│ │ └── day2
│ │ └── session2
│ │ ├── ClientDisplay.scala
│ │ ├── ClientInput.scala
│ │ ├── ConsoleGame.scala
│ │ ├── Example1LocalActor.scala
│ │ ├── Example2Messages.scala
│ │ ├── Example3Callbacks.scala
│ │ ├── Example4RemoteActorSameJVM.scala
│ │ ├── Example5RemoteActorNewJVM.scala
│ │ ├── GameActor.scala
│ │ └── GameClient.scala
├── session3
│ ├── 1-lift
│ │ └── README
│ ├── 2-scalaz
│ │ └── README
│ ├── 3-scala-gui
│ │ └── src
│ │ │ └── main
│ │ │ └── scala
│ │ │ └── org
│ │ │ └── frontierdevelopers
│ │ │ └── scalaworkshop
│ │ │ └── gui
│ │ │ ├── Example1_Button.scala
│ │ │ ├── Example2_Button.scala
│ │ │ ├── Example3_Layout.scala
│ │ │ ├── Example4_Events.scala
│ │ │ ├── Example5_Java2D.scala
│ │ │ └── Example6_PopQuiz.scala
│ └── README
└── session4
│ └── README
├── derek-lift-demo
├── .gitignore
├── project
│ ├── build.properties
│ └── build
│ │ └── LiftProject.scala
├── sbt
├── sbt-launcher.jar
├── sbt.bat
└── src
│ ├── main
│ ├── resources
│ │ ├── .keep
│ │ └── props
│ │ │ └── default.props
│ ├── scala
│ │ ├── bootstrap
│ │ │ └── liftweb
│ │ │ │ └── Boot.scala
│ │ ├── code
│ │ │ ├── comet
│ │ │ │ └── GameDisplay.scala
│ │ │ ├── lib
│ │ │ │ └── DependencyFactory.scala
│ │ │ ├── model
│ │ │ │ └── .keep
│ │ │ ├── snippet
│ │ │ │ └── HelloWorld.scala
│ │ │ ├── util
│ │ │ │ ├── BridgeActor.scala
│ │ │ │ └── Controller.scala
│ │ │ └── view
│ │ │ │ └── .keep
│ │ └── org
│ │ │ └── frontierdevelopers
│ │ │ └── scalaworkshop
│ │ │ └── day2
│ │ │ └── session2
│ │ │ ├── ClientMessages.scala
│ │ │ ├── GameActions.scala
│ │ │ ├── GameActor.scala
│ │ │ └── GameState.scala
│ └── webapp
│ │ ├── WEB-INF
│ │ └── web.xml
│ │ ├── images
│ │ └── ajax-loader.gif
│ │ ├── index.html
│ │ ├── static
│ │ └── index.html
│ │ └── templates-hidden
│ │ ├── default.html
│ │ └── wizard-all.html
│ └── test
│ ├── resources
│ └── .keep
│ └── scala
│ ├── LiftConsole.scala
│ ├── RunWebApp.scala
│ └── code
│ ├── AppTest.scala
│ ├── XmlSourceSpecs.scala
│ └── snippet
│ └── HelloWorldTest.scala
├── preso
├── Lift_Anatomy.odg
└── lift-first-screenshot.png
├── sample_game
├── README
├── project
│ ├── build.properties
│ ├── build
│ │ └── Dungeon.scala
│ └── plugins
│ │ └── Plugins.scala
└── src
│ ├── main
│ └── scala
│ │ └── Dungeon.scala
│ └── test
│ └── scala
│ └── DungeonSpec.scala
├── sandbox
└── LearnScala
│ ├── .gitignore
│ ├── project
│ ├── build.properties
│ ├── build
│ │ └── LearnScalaProject.scala
│ └── plugins
│ │ └── Plugins.scala
│ └── src
│ └── main
│ ├── resources
│ └── logback.xml
│ └── scala
│ └── org
│ └── frontier
│ ├── Character.scala
│ ├── Creature.scala
│ ├── GameClient.scala
│ ├── GameController.scala
│ ├── GameServer.scala
│ ├── Item.scala
│ └── Location.scala
└── scala-workshop.iml
/.gitignore:
--------------------------------------------------------------------------------
1 | boot/
2 | lib_managed/
3 | src_managed/
4 | target/
5 | .idea/
6 | project.iml
7 | .DS_Store
8 | *.iml
9 |
10 |
--------------------------------------------------------------------------------
/day1/session1/README:
--------------------------------------------------------------------------------
1 | Readme for scala-workshop
2 |
3 | Sam Reid
4 | 3/20/2011
--------------------------------------------------------------------------------
/day1/session1/project/build.properties:
--------------------------------------------------------------------------------
1 | #Project properties
2 | #Sat Apr 09 11:41:31 MDT 2011
3 | project.organization=frontier-developers
4 | project.name=scala-workshop
5 | sbt.version=0.7.4
6 | project.version=1.0
7 | build.scala.versions=2.8.1
8 | project.initialize=false
9 |
--------------------------------------------------------------------------------
/day1/session1/project/build/Project.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 |
3 | class Project(info: ProjectInfo) extends DefaultProject(info) with IdeaProject {
4 | val scala_swing = "org.scala-lang" % "scala-swing" % "2.8.1"
5 | }
6 |
7 | // vim: set ts=4 sw=4 et:
8 |
--------------------------------------------------------------------------------
/day1/session1/project/plugins/Plugins.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
3 | val sbtIdeaRepo = "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
4 | val sbtIdea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.4.0"
5 | }
6 |
--------------------------------------------------------------------------------
/day1/session1/scala-workshop.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example10_Lazy.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | object Example10_Lazy {
4 | val a = 999
5 | lazy val b = {
6 | println("hello")
7 | 123
8 | }
9 |
10 | def main(args: Array[String]) {
11 | println(a)
12 | println(b)
13 | }
14 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example11_Traits.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | trait MyItem {
4 | val name: String
5 | val weight = 1
6 | }
7 |
8 | object Example11_Traits {
9 | def main(args: Array[String]) {
10 |
11 | object sword extends MyItem {
12 | val name = "sword of wonder"
13 | }
14 | println(sword.name)
15 | println(sword.weight)
16 | }
17 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example12_Inheritance.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | class Beast {}
4 |
5 | trait Huge {
6 | val attackMultiplier = 3
7 | }
8 |
9 | trait Ugly {
10 | val fearMultiplier = 5
11 | }
12 |
13 | object HugeUglyBeast extends Beast with Huge with Ugly
14 |
15 | object Example12_Inheritance {
16 | def main(args: Array[String]) {
17 | println("the fear multiplier = " + HugeUglyBeast.fearMultiplier)
18 | }
19 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example13_CaseClasses.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | trait Item {val name: String}
4 |
5 | case class Weapon(name: String, attack: Int)
6 | extends Item
7 |
8 | object Example13_CaseClasses {
9 | def main(args: Array[String]) {
10 | val sword = new Weapon("sword of wonder", 999)
11 | println(sword)
12 | }
13 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example14_Types.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | object Example14_Types {
4 | def main(args: Array[String]) {
5 |
6 | object HugeUglyMonster extends Monster("Troll", "Glurk") with Huge with Ugly
7 |
8 | val hum: HugeUglyMonster.type = HugeUglyMonster
9 | val mhu: Monster with Huge with Ugly = HugeUglyMonster
10 | val monster: Monster = HugeUglyMonster //or Mr. Wiggles
11 | val huge: Huge = HugeUglyMonster //or Mr. Wiggles
12 | val ugly: Ugly = HugeUglyMonster //but not Mr. Wiggles!
13 |
14 | }
15 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example1_Values.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | /**
4 | * @author Sam Reid
5 | */
6 |
7 | object Example1_Values {
8 | def main(args: Array[String]) {
9 | val species = "Grue"
10 | var health = Int.MaxValue
11 |
12 | // initial health equals max health
13 | val maxHealth = health
14 | val attack = 42000000
15 |
16 | // Definitely not friendly
17 | val friendly = false
18 | println(species)
19 | println(attack)
20 | }
21 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example2_Evolve.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | object Example2_Evolve {
4 | def main(args: Array[String]) {
5 | val species = "Grue"
6 | var health = Int.MaxValue
7 | val maxHealth = health
8 | // to start
9 | val attack = 42000000
10 | val friendly = false
11 | // Definitely not
12 | println("initial health = " + Grue.health)
13 | Grue.health = 123
14 | println("changed health = " + Grue.health)
15 | //Won't compile
16 | //Grue.friendly = true
17 | }
18 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example3_OurFirstMonster.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | object Grue {
4 | val species = "Grue"
5 | var health = Int.MaxValue
6 | val maxHealth = health
7 | val attack = 42000000
8 | val friendly = false
9 | }
10 |
11 | object Example3_OurFirstMonster {
12 | def main(args: Array[String]) {
13 | println(Grue.attack)
14 | }
15 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example4_MultipleMonsters.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | class Dragon {
4 | val species = "Dragon"
5 | var health = Int.MaxValue
6 | val maxHealth = health
7 | val attack = 42000000
8 | val friendly = false
9 | }
10 |
11 | object Example4_MultipleMonsters {
12 | def main(args: Array[String]) {
13 | val myPetDragon = new Dragon
14 | println(myPetDragon)
15 | println(myPetDragon.attack)
16 | }
17 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example5_ClassesVsInstances.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | class MyCreature2(val species: String,
4 | var health: Int,
5 | val attack: Int,
6 | val friendly: Boolean) {
7 | val maxHealth = health
8 | }
9 |
10 | object Example5_ClassesVsInstances {
11 | def main(args: Array[String]) {
12 | val myPetGrue = new MyCreature2("Grue", Int.MaxValue, 42000000, false)
13 | println(myPetGrue)
14 | println(myPetGrue.species)
15 | }
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example6_NamedAndDefault.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | class Creature(val species: String,
4 | var health: Int = 50,
5 | val attack: Int = 5,
6 | val friendly: Boolean = false) {
7 | val maxHealth = health
8 | }
9 |
10 | object Example6_NamedAndDefault {
11 | def main(args: Array[String]) {
12 | val myPetRabbit = new Creature("Rabbit", 12, friendly = true)
13 | println(myPetRabbit.attack)
14 | }
15 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example7_Specializing.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | class Monster(species: String,
4 | val name: String,
5 | health: Int = 50,
6 | attack: Int = 10,
7 | val intelligence: Int = 10)
8 | extends Creature(species, health, attack, false)
9 |
10 | object Example7_Specializing {
11 | def main(args: Array[String]) {
12 | val monster = new Monster("centaur", "larry", attack = 999, intelligence = 2)
13 | println(monster)
14 | }
15 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example8_Abstract.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | abstract class MyCreature(val species: String,
4 | var health: Int)
5 |
6 | object Example8_Abstract {
7 | def main(args: Array[String]) {
8 | //won't compile:
9 | // val x = new Creature("genie",999)
10 | }
11 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/Example9_Alignment.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | sealed abstract class Alignment
4 |
5 | object Lawful extends Alignment
6 |
7 | object Neutral extends Alignment
8 |
9 | object Chaotic extends Alignment
10 |
11 | object Example9_Alignment {
12 | def main(args: Array[String]) {
13 |
14 | }
15 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/datamodeling/TestPrivate.scala:
--------------------------------------------------------------------------------
1 | package org.datamodeling
2 |
3 | object TestPrivate {
4 | def main(args: Array[String]) {
5 | class Creature(val species: String,
6 | private var health: Int)
7 |
8 | val c = new Creature("dragon",123)
9 | c.species
10 | }
11 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/fdgame/solutions/Armor.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.fdgame.solutions
5 |
6 |
7 | case class Armor(val name : String, protection : Int, weight : Double = 5, description : String) extends Item
8 |
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/fdgame/solutions/Character.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.fdgame.solutions
5 |
6 | case class Character(name : String,
7 | var health : Int,
8 | var items : List[Item] = Nil,
9 | var armor : Option[Armor] = None,
10 | var weapon : Option[Weapon] = None)
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/fdgame/solutions/Container.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.fdgame.solutions
5 |
6 |
7 | case class Container(name : String, contents : List[Item]) extends Item {
8 | val description = "A container"
9 | val weight = contents.map(_.weight).sum
10 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/fdgame/solutions/Item.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.fdgame.solutions
5 |
6 | /**
7 | * The base trait for items.
8 | */
9 | trait Item {
10 | val name : String
11 | val description : String
12 | def weight : Double
13 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/fdgame/solutions/Key.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.fdgame.solutions
5 |
6 |
7 | case class Key(name : String, weight : Double = 0.1, description : String) extends Item
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/fdgame/solutions/Potion.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.fdgame.solutions
5 |
6 |
7 | case class Potion(name : String, color : String, healing : Int) extends Item {
8 | val weight = 0.25
9 | val description = "A %s potion".format(color)
10 | }
11 |
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/fdgame/solutions/Weapon.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.fdgame.solutions
5 |
6 |
7 | case class Weapon(name : String, damage : Double, weight : Double = 1, description : String) extends Item
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/frontierdevelopers/scalaworkshop/ConsoleGame.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop
2 |
3 | import java.lang.Integer
4 |
5 | case class Player(name: String, gold: Integer, location: Site, items: List[GameObject]) {
6 | val weapons = Nil
7 |
8 | override def toString = name + ", gold=" + gold + ", location = " + location
9 | }
10 |
11 | trait GameObject {
12 | def actions: List[Action]
13 |
14 | def getSiteDescription: String
15 |
16 | def getInventoryDescription: String
17 | }
18 |
19 | case class Site(name: String, description: String, var objects: List[GameObject] = Nil) {
20 | def this(name: String, description: String) = this (name, description, Nil)
21 | }
22 |
23 | case class BlueKey extends GameObject {
24 | def actions() = new Action() {
25 | def update(state: GameState) = {
26 | val updatedJail: Site = GameMap.jail.copy(objects = Nil)
27 | (
28 | state.copy(
29 | player = state.player.copy(items = BlueKey.this :: state.player.items, location = updatedJail),
30 | map = state.map.update(GameMap.jail, updatedJail)
31 | ),
32 | "You pick up the Blue key, dust it off and put it in your pocket"
33 | )
34 | }
35 |
36 | override def toString = "Take the key when nobody is looking."
37 | } :: Nil
38 |
39 | def getInventoryDescription = "The blue key from the Scalata Jail"
40 |
41 | def getSiteDescription = "You see a blue key on the stone floor, shimmering in the moonlight."
42 | }
43 |
44 | trait Action {
45 | def update(state: GameState): (GameState, String)
46 | }
47 |
48 | class ExitAction extends Action {
49 | def update(state: GameState) = {
50 | System.exit(0)
51 | null
52 | }
53 |
54 | override def toString = "exit game"
55 | }
56 |
57 | case class GameMap(links: Map[Site, List[Site]]) {
58 | def update(old: Site, newsite: Site ): GameMap = GameMap(
59 | links.foldLeft(Map.empty[Site, List[Site]]) {
60 | case (m, (from, to)) => m + ((if (from == old) newsite else old) -> to.map(t => if (t == old) newsite else old))
61 | }
62 | )
63 | }
64 |
65 | object GameMap {
66 | val jail = Site("Scalata jail", "Jail in Scalata. This place reeks of justice", new BlueKey :: Nil)
67 | val townSquare = Site("Scalata town square", "Town square in Scalata, a nice village with elves or equivalent. Merchants line the streets and a small protest is forming.")
68 | val clearing = Site("Clearing by a pond", "Clearing, near a pond with deer grazing nearby. No sign of snakes, but there is a foreboding and smelly cave nearby.")
69 | val cave = Site("Cave", "Cavernous cave. A scorpion runs past you, screaming.")
70 |
71 | val initialMap = GameMap(
72 | Map(
73 | townSquare -> ( clearing :: jail :: Nil ),
74 | clearing -> ( townSquare :: cave :: Nil ),
75 | jail -> ( townSquare :: Nil ),
76 | cave -> ( clearing :: Nil )
77 | )
78 | )
79 | }
80 |
81 |
82 | //case class GameMap(jail: Site = new Site("Scalata jail", "Jail in Scalata. This place reeks of justice", new BlueKey :: Nil)) {
83 | // val townSquare = new Site("Scalata town square", "Town square in Scalata, a nice village with elves or equivalent. Merchants line the streets and a small protest is forming.")
84 | // val clearing = new Site("Clearing by a pond", "Clearing, near a pond with deer grazing nearby. No sign of snakes, but there is a foreboding and smelly cave nearby.")
85 | // val cave = new Site("Cave", "Cavernous cave. A scorpion runs past you, screaming.")
86 | // val links = Map(townSquare -> ( clearing :: jail :: Nil ),
87 | // clearing -> ( townSquare :: cave :: Nil ),
88 | // jail -> ( townSquare :: Nil ),
89 | // cave -> ( Nil )
90 | // )
91 | //}
92 |
93 | case class GameState(map: GameMap, player: Player) {
94 | def this(map: GameMap) = this (map, new Player("Dorbax", 0, GameMap.townSquare, Nil))
95 |
96 |
97 | def update(input: Action) = input.update(this)
98 |
99 | def choices = {
100 | val travelLinks = for ( link <- map.links(player.location) ) yield {
101 | TravelTo(link)
102 | }
103 | val itemChoices = for ( obj <- player.location.objects; action <- obj.actions ) yield {
104 | action
105 | }
106 | val systemChoices = new ExitAction :: Nil
107 | travelLinks ::: itemChoices.toList ::: systemChoices
108 | }
109 |
110 | override def toString = {
111 | player.name + ": " + player.gold + " gold, you have: " + player.items.map(_.getInventoryDescription).mkString(",") + "\n" +
112 | player.location.name + "\n" +
113 | player.location.description + "\n" +
114 | player.location.objects.map(_.getSiteDescription).mkString("\n")
115 | }
116 | }
117 |
118 | case class TravelTo(destination: Site) extends Action {
119 | override def toString = "Travel to " + destination.name
120 |
121 | def update(state: GameState) = (state.copy(player = state.player.copy(location = destination)), "You travel to " + destination.name)
122 | }
123 |
124 | class ConsoleGame {
125 | def loopGame(state: GameState): Unit = {
126 | println(state)
127 | val choices = state.choices
128 | val numberedChoices = for ( i <- 0 until choices.length ) yield {
129 | i + ". " + choices(i)
130 | }
131 | println(numberedChoices.mkString("\n"))
132 | val line: String = readLine()
133 | val choice = Integer.parseInt(line)
134 | val result: (GameState, String) = state.update(choices(choice))
135 | println(result._2)
136 | loopGame(result._1)
137 | }
138 | }
139 |
140 | object ConsoleGame {
141 | def main(args: Array[String]) {
142 | new ConsoleGame().loopGame(new GameState(GameMap.initialMap))
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/frontierdevelopers/scalaworkshop/NullAndOption.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop
2 |
3 | import org.fdgame.solutions.{Armor, Item}
4 |
5 | case class NullableCharacter(name : String,
6 | var health : Int = 30,
7 | var items : List[Item] = Nil,
8 | var armor : Armor = null)
9 |
10 | object NullArmor {
11 | def main (args : Array[String]) {
12 | val fred = NullableCharacter("Fred")
13 |
14 | val attack = 12
15 |
16 | println("Net damage = " + (attack - fred.armor.protection))
17 | }
18 | }
19 |
20 | case class OptionCharacter(name : String,
21 | var health : Int = 30,
22 | var items : List[Item] = Nil,
23 | var armor : Option[Armor] = None)
24 |
25 | object OptionArmor {
26 | def main (args : Array[String]) {
27 | val ted = OptionCharacter("Ted")
28 |
29 | val attack = 12
30 |
31 | println("Net damage = " + (attack -
32 | ted.armor.map(_.protection).getOrElse(0)))
33 | }
34 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/frontierdevelopers/scalaworkshop/PuttingItAllTogether.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop
2 |
3 | /**
4 | * @author Sam Reid
5 | */
6 |
7 | class PuttingItAllTogether
8 |
9 | //Putting it all together
10 | case class Key(color: String)
11 |
12 | case class Potion(healing: Int)
13 |
14 | case class Game {
15 | val player = new Player2
16 | val worldMap = Map("jail" -> List("town square"),
17 | "town square" -> List("jail", "cave", "Scalata"))
18 |
19 | override def toString = "player = " + player + ", map = " + worldMap
20 | }
21 |
22 | case class Player2 {
23 | val name = "hero"
24 | var items = Potion(-5) :: Potion(123) :: Key("blue") :: Nil
25 |
26 | override def toString = "name = " + name + ", items: " + items.mkString(", ")
27 | }
28 |
29 | object TestMain {
30 | def main(args: Array[String]) {
31 | println(new Game)
32 | }
33 | }
34 |
35 | object Grue{
36 | val species = "Grue"
37 | val strength = 456
38 | val maxHealth = 200
39 | val health = maxHealth
40 | val attackDamage = 42000000
41 | val friendly = false
42 | }
43 |
44 | object MyAwesomeGame{
45 | def main(args: Array[String]) {
46 | println(Grue)
47 | }
48 | }
--------------------------------------------------------------------------------
/day1/session1/src/main/scala/org/testgame/SwordWarrior.scala:
--------------------------------------------------------------------------------
1 | package org.testgame
2 |
3 | trait Named {
4 | def name: String
5 | }
6 | trait Described {
7 | def description: String
8 | }
9 |
10 | trait Item extends Named with Described
11 |
12 | object Items {
13 | val HealthPotion = Potion("A health potion", "A large, redish colored potion capable of restoring health to whoever consumes it")
14 | }
15 |
16 | case class Potion(name: String, description: String) extends Item
17 |
18 | case class Container(items: Map[Item, Int]) {
19 | def + (item: Item): Container = Container(items.get(item) match {
20 | case None => items + (item -> 1)
21 | case Some(count) => items + (item -> (count + 1))
22 | })
23 |
24 | def - (item: Item): Container = items.get(item) match {
25 | case None => this
26 |
27 | case Some(count) if (count <= 1) => Container(items - item)
28 |
29 | case Some(count) => Container(items + (item -> (count - 1)))
30 | }
31 | }
32 | object Container {
33 | def empty: Container = Container(Map.empty[Item, Int])
34 | }
35 |
36 | case class LocationId(name: String) extends Named
37 | case class Location(id: LocationId, description: String, items: Container) extends Named with Described {
38 | def name: String = id.name
39 | }
40 |
41 | object Locations {
42 | val GrassyFields = Location(LocationId("grassy fields"),
43 | "You are standing in grassy fields. The hot sun is beating down on you.",
44 | Container.empty + Items.HealthPotion
45 | )
46 | }
47 |
48 | case class Health(value: Int)
49 | object Health {
50 | val Min = Health(0)
51 | val Max = Health(100)
52 | }
53 | sealed trait Character {
54 | def health: Int
55 | def inventory: Container
56 | }
57 | case class Player(name: String, health: Health, location: Location, inventory: Container)
58 |
59 | case class World(
60 | player: Player
61 | ) {
62 | def actions: List[Action] = Exit :: Look :: pickupActions
63 |
64 | private def pickupActions: List[Action] = {
65 | player.location.items.items.map {
66 | case ((item, count)) => Pickup(item)
67 | }.toList
68 | }
69 | }
70 | object World {
71 | def apply(name: String): World =
72 | World(Player(
73 | name = name,
74 | health = Health.Max,
75 | location = Locations.GrassyFields,
76 | inventory = Container.empty
77 | ))
78 | }
79 |
80 | sealed trait Action extends (World => World) with Described
81 |
82 | case class Pickup(item: Item) extends Action {
83 | def description = "Pick up " + item.name
84 |
85 | def apply(old: World): World = {
86 | import old.player
87 | import player.inventory
88 | import player.location
89 |
90 | old.copy(
91 | player = player.copy(
92 | inventory = inventory + item,
93 | location = location.copy(
94 | items = location.items - item
95 | )
96 | )
97 | )
98 | }
99 | }
100 |
101 | case object Look extends Action {
102 | def description = "Look around"
103 |
104 | def apply(old: World): World = {
105 | println(old.player.location.description)
106 |
107 | old
108 | }
109 | }
110 |
111 | case object Exit extends Action {
112 | def description = "Exit the game"
113 |
114 | def apply(old: World): World = {
115 | println("Are you sure you want to exit the game? (y/n)")
116 |
117 | readChar().toLower match {
118 | case 'y' => System.exit(0)
119 | }
120 |
121 | old
122 | }
123 | }
124 |
125 | object SwordWarrior {
126 | def main(args: Array[String]) {
127 | println("Welcome to SwordWarrioer!")
128 | println("What is your name? ")
129 |
130 | val name = readLine()
131 |
132 | println("Welcome, " + name)
133 |
134 | val world = World(name)
135 |
136 | run(world)
137 | }
138 |
139 | def run(world: World): World = {
140 | println("What would you like to do now? ")
141 |
142 | val actions = world.actions
143 |
144 | actions.zipWithIndex.foreach {
145 | case (action, index) =>
146 | println(index + ". " + action.description)
147 | }
148 |
149 | val newWorld = (readInt() match {
150 | case choice if (choice < 0 || choice >= actions.length) =>
151 | println("That is not a choice you have, " + world.player.name)
152 | world
153 |
154 | case choice =>
155 | val chosenAction = actions(choice)
156 |
157 | println("You have chosen to: " + chosenAction.description)
158 |
159 | chosenAction(world)
160 | })
161 |
162 | run(newWorld)
163 | }
164 | }
--------------------------------------------------------------------------------
/day1/session2/README:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/day1/session2/README
--------------------------------------------------------------------------------
/day1/session2/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | fd.dungeon
5 | dungeon_2.8.1
6 | jar
7 | 1.0
8 |
9 |
10 | org.scala-lang
11 | scala-library
12 | 2.8.1
13 |
14 |
15 |
16 |
17 | ScalaToolsMaven2Repository
18 | Scala-Tools Maven2 Repository
19 | http://scala-tools.org/repo-releases/
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/day1/session2/project/build.properties:
--------------------------------------------------------------------------------
1 | #Project properties
2 | #Wed Apr 27 11:24:00 MDT 2011
3 | project.organization=fd.dungeon
4 | project.name=dungeon
5 | sbt.version=0.7.5
6 | project.version=1.0
7 | build.scala.versions=2.8.1
8 | project.initialize=false
9 |
--------------------------------------------------------------------------------
/day1/session2/project/build/Project.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 |
3 | class Project(info: ProjectInfo) extends DefaultProject(info) with IdeaProject {
4 | }
5 |
6 | // vim: set ts=4 sw=4 et:
7 |
--------------------------------------------------------------------------------
/day1/session2/project/plugins/Plugins.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
3 | val sbtIdeaRepo = "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
4 | val sbtIdea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.4.0"
5 | }
6 |
--------------------------------------------------------------------------------
/day1/session2/src/main/scala/Functions.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by IntelliJ IDEA.
3 | * User: knuttycombe
4 | * Date: 4/27/11
5 | * Time: 11:34 AM
6 | * To change this template use File | Settings | File Templates.
7 | */
8 |
9 | class Item(val name: String, val cursed: Boolean)
10 |
11 | object Items {
12 |
13 | val hat: Item = new Item("Hat", true)
14 |
15 | val message: String = if (hat.cursed) {
16 | "That hurts to touch."
17 | } else {
18 | "Seems okay."
19 | }
20 |
21 | val item: Item = new Item("Hat", true)]
22 |
23 | val msg = if (item.cursed) "Ouch!" else "What?"
24 |
25 | //Multiline works okay too:
26 |
27 | val msg = if (item.cursed) "Ouch!"
28 | else "What?"
29 |
30 |
31 | val message: Any = if (item.cursed) {
32 | "This thing hurts!"
33 | }
34 |
35 | var message: String = _
36 | var message2: String = _
37 |
38 | if (item.cursed) {
39 | message = "That hurts to touch."
40 | message2 = "You think it might be cursed?"
41 | } else {
42 | message = "Seems okay."
43 | message2 = "Doesn't look like it'll bite."
44 | }
45 |
46 | }
47 |
48 | object Functions {
49 |
50 | ((i: Int) => i + 1)
51 |
52 |
53 | val inc = (i: Int) => i + 1
54 | val inc2 = (_: Int) + 1
55 |
56 |
57 | val add = (i: Int, j: Int) => i + j
58 | val add2 = (_: Int) + (_: Int)
59 | }
60 |
61 | object Functions2 {
62 |
63 |
64 | trait Function1[-A, +B] {
65 | def apply(a: A): B
66 | }
67 |
68 | val add1 = (i1: Int, i2: Int) => i1 + i2
69 | val add2 = new Function2[Int, Int, Int] {
70 | def apply(i1: Int, i2: Int): Int = i1 + i2
71 | }
72 |
73 | class Item(
74 | val name: String,
75 | val weight: Int,
76 | val cursed: Boolean = false,
77 | val durability: Int = 2,
78 | val attack: Int = 0,
79 | val magic: Option[Magic] = None
80 | )
81 |
82 | object Item {
83 | val show: Item => String = (item: Item) =>
84 | item.name + " (weight: "+item.weight+")"
85 |
86 | val showTruth = (item: Item) => {
87 | if (item.cursed) "CURSED " + show(item)
88 | else show(item)
89 | }
90 | }
91 |
92 | object WandOfWonder extends Item(
93 | "Wand of Wonder", 2
94 | )
95 |
96 | // Explicit call to apply
97 | Item.show.apply(WandOfWonder)
98 |
99 | // The compiler will insert .apply for us
100 | Item.show(WandOfWonder)
101 | }
--------------------------------------------------------------------------------
/day1/session4/README:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/day1/session4/README
--------------------------------------------------------------------------------
/day2/session1/README:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/day2/session1/README
--------------------------------------------------------------------------------
/day2/session2/README:
--------------------------------------------------------------------------------
1 | Concurrency
2 | Sam & Derek
--------------------------------------------------------------------------------
/day2/session2/data/akka.conf:
--------------------------------------------------------------------------------
1 | ####################
2 | # Akka Config File #
3 | ####################
4 |
5 | # This file has all the default settings, so all these could be removed with no visible effect.
6 | # Modify as needed.
7 |
8 | akka {
9 | version = "1.0" # Akka version, checked against the runtime version of Akka.
10 |
11 | time-unit = "seconds" # Default timeout time unit for all timeout properties throughout the config
12 |
13 | # These boot classes are loaded (and created) automatically when the Akka Microkernel boots up
14 | # Can be used to bootstrap your application(s)
15 | # Should be the FQN (Fully Qualified Name) of the boot class which needs to have a default constructor
16 | boot = ["sample.camel.Boot",
17 | "sample.rest.java.Boot",
18 | "sample.rest.scala.Boot",
19 | "sample.security.Boot"]
20 |
21 | actor {
22 | timeout = 5 # Default timeout for Future based invocations
23 | # - Actor: !! && !!!
24 | # - UntypedActor: sendRequestReply && sendRequestReplyFuture
25 | # - TypedActor: methods with non-void return type
26 | serialize-messages = off # Does a deep clone of (non-primitive) messages to ensure immutability
27 | throughput = 5 # Default throughput for all ExecutorBasedEventDrivenDispatcher, set to 1 for complete fairness
28 |
29 | default-dispatcher {
30 | type = "GlobalExecutorBasedEventDriven" # Must be one of the following, all "Global*" are non-configurable
31 | # - Hawt
32 | # - ExecutorBasedEventDriven
33 | # - ExecutorBasedEventDrivenWorkStealing
34 | # - ReactorBasedThreadPoolEventDriven
35 | # - ReactorBasedSingleThreadEventDriven
36 | # - GlobalHawt
37 | # - GlobalExecutorBasedEventDriven
38 | # - GlobalReactorBasedSingleThreadEventDriven
39 | # - GlobalReactorBasedThreadPoolEventDriven
40 | keep-alive-ms = 60000 # Keep alive time for threads
41 | core-pool-size-factor = 1.0 # No of core threads ... ceil(available processors * factor)
42 | max-pool-size-factor = 4.0 # Max no of threads ... ceil(available processors * factor)
43 | executor-bounds = -1 # Makes the Executor bounded, -1 is unbounded
44 | allow-core-timeout = on # Allow core threads to time out
45 | rejection-policy = "caller-runs" # abort, caller-runs, discard-oldest, discard
46 | throughput = 5 # Throughput for ExecutorBasedEventDrivenDispatcher, set to 1 for complete fairness
47 | aggregate = off # Aggregate on/off for HawtDispatchers
48 | mailbox-capacity = -1 # If negative (or zero) then an unbounded mailbox is used (default)
49 | # If positive then a bounded mailbox is used and the capacity is set using the property
50 | # NOTE: setting a mailbox to 'blocking' can be a bit dangerous,
51 | # could lead to deadlock, use with care
52 | }
53 | }
54 |
55 | stm {
56 | fair = on # Should global transactions be fair or non-fair (non fair yield better performance)
57 | jta-aware = off # Option 'on' means that if there JTA Transaction Manager available then the STM will
58 | # begin (or join), commit or rollback the JTA transaction. Default is 'off'.
59 | timeout = 5 # Default timeout for blocking transactions and transaction set (in unit defined by
60 | # the time-unit property)
61 | }
62 |
63 | jta {
64 | provider = "from-jndi" # Options: - "from-jndi" (means that Akka will try to detect a TransactionManager in the JNDI)
65 | # - "atomikos" (means that Akka will use the Atomikos based JTA impl in 'akka-jta',
66 | # e.g. you need the akka-jta JARs on classpath).
67 | timeout = 60
68 | }
69 |
70 | rest {
71 | service = on
72 | hostname = "localhost"
73 | port = 9998
74 | filters = ["se.scalablesolutions.akka.security.AkkaSecurityFilterFactory"] # List with all jersey filters to use
75 | resource_packages = ["sample.rest.scala",
76 | "sample.rest.java",
77 | "sample.security"] # List with all resource packages for your Jersey services
78 | authenticator = "sample.security.BasicAuthenticationService" # The authentication service to use. Need to be overridden (sample now)
79 |
80 | comet-dispatcher {
81 | #type = "Hawt" # Uncomment if you want to use a different dispatcher than the default one for Comet
82 | }
83 | # maxInactiveActivity = 60000 # Atmosphere CometSupport maxInactiveActivity
84 |
85 | # Uncomment if you are using the KerberosAuthenticationActor
86 | # kerberos {
87 | # servicePrincipal = "HTTP/localhost@EXAMPLE.COM"
88 | # keyTabLocation = "URL to keytab"
89 | # kerberosDebug = "true"
90 | # realm = "EXAMPLE.COM"
91 | # }
92 | }
93 |
94 | remote {
95 | compression-scheme = "zlib" # Options: "zlib" (lzf to come), leave out for no compression
96 | zlib-compression-level = 6 # Options: 0-9 (1 being fastest and 9 being the most compressed), default is 6
97 |
98 | ssl {
99 | service = off # NOTE: This feature is not deemed production ready and is not possible to turn on yet
100 |
101 | # You can either use java command-line options or use the settings below
102 |
103 | #key-store-type = "pkcs12" # Same as -Djavax.net.ssl.keyStoreType=pkcs12
104 | #key-store = "yourcertificate.p12" # Same as -Djavax.net.ssl.keyStore=yourcertificate.p12
105 | #key-store-pass = "$PASS" # Same as -Djavax.net.ssl.keyStorePassword=$PASS
106 |
107 | #trust-store-type = "jks" # Same as -Djavax.net.ssl.trustStoreType=jks
108 | #trust-store = "your.keystore" # Same as -Djavax.net.ssl.trustStore=your.keystore
109 | #trust-store-pass = "$PASS" # Same as -Djavax.net.ssl.trustStorePassword=$PASS
110 |
111 | debug = off # This can be useful for debugging. If on, very verbose debug, same as -Djavax.net.debug=ssl
112 | }
113 |
114 | server {
115 | service = on
116 | hostname = "localhost" # The hostname or IP that clients should connect to
117 | port = 9999 # The port clients should connect to
118 | connection-timeout = 1
119 | }
120 |
121 | client {
122 | reconnect-delay = 5
123 | read-timeout = 10
124 | reconnection-time-window = 600 # Maximum time window that a client should try to reconnect for
125 | }
126 |
127 | cluster {
128 | service = on
129 | name = "default" # The name of the cluster
130 | serializer = "se.scalablesolutions.akka.serialization.Serializer$Java$" # FQN of the serializer class
131 | }
132 | }
133 |
134 | storage {
135 | cassandra {
136 | hostname = "127.0.0.1" # IP address or hostname of one of the Cassandra cluster's seeds
137 | port = 9160 # Port to Cassandra
138 | consistency-level = "QUORUM" # Options: ZERO, ONE, QUORUM, DCQUORUM, DCQUORUMSYNC, ALL, ANY
139 | }
140 |
141 | mongodb {
142 | hostname = "127.0.0.1" # IP address or hostname of the MongoDB DB instance
143 | port = 27017 # Port to MongoDB
144 | dbname = "mydb"
145 | }
146 |
147 | redis {
148 | hostname = "127.0.0.1" # IP address or hostname of the Redis instance
149 | port = 6379 # Port to Redis
150 | }
151 | }
152 | }
--------------------------------------------------------------------------------
/day2/session2/data/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | [%4p] [%d{ISO8601}] [%t] %c{1}: %m%n
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/day2/session2/project/build.properties:
--------------------------------------------------------------------------------
1 | #Project properties
2 | #Mon Apr 25 16:36:43 MDT 2011
3 | project.organization=org.fdgame
4 | project.name=workshop-d2s2
5 | sbt.version=0.7.5
6 | project.version=1.0
7 | build.scala.versions=2.8.1
8 | project.initialize=false
9 |
--------------------------------------------------------------------------------
/day2/session2/project/build/ConcurrencyProject.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 |
5 | import sbt._
6 |
7 | class ConcurrencyProject(info : ProjectInfo) extends DefaultProject(info) {
8 | val akka_repo = "Akka Maven Repository" at "http://akka.io/repository"
9 | val jboss_repo = "JBoss Maven Repository" at "http://repository.jboss.org/nexus/content/groups/public/"
10 | val guiceyfruit_repo = "GuiceyFruit repo" at "http://guiceyfruit.googlecode.com/svn/repo/releases/"
11 |
12 | val akka = "se.scalablesolutions.akka" % "akka-actor" % "1.0"
13 | val akkaRemote = "se.scalablesolutions.akka" % "akka-remote" % "1.0"
14 | }
15 |
--------------------------------------------------------------------------------
/day2/session2/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | [%4p] [%d{ISO8601}] [%t] %c{1}: %m%n
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/day2/session2/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/ClientDisplay.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.frontierdevelopers.scalaworkshop.day2.session2
5 |
6 | import akka.actor.Actor
7 |
8 | class ClientDisplay extends Actor {
9 | def receive = {
10 | case Display(message) => println(message)
11 | }
12 | }
--------------------------------------------------------------------------------
/day2/session2/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/ClientInput.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.frontierdevelopers.scalaworkshop.day2.session2
5 |
6 | import akka.actor.Actor
7 | import annotation.tailrec
8 |
9 | class ClientInput extends Actor {
10 | var name = ""
11 |
12 | def receive = {
13 | case Prompt(message, choices) => {
14 | println("\n" + message)
15 |
16 | val choice = readChoice(choices)
17 | choice.agent ! ClientChoice(name, choice.action)
18 | }
19 | case name : String => this.name = name
20 | }
21 |
22 | @tailrec
23 | final def readChoice(choices : List[Choice]) : Choice = {
24 | println("\nYou may:\n")
25 |
26 | choices.zipWithIndex.foreach {
27 | case (choice : Choice,index : Int) => println((index +1) + ". " + choice.description)
28 | }
29 |
30 | print("\nChoice: ")
31 |
32 | try {
33 | return choices(readLine().toInt - 1)
34 | } catch {
35 | case e : Exception => println("That isn't a valid choice")
36 | }
37 |
38 | readChoice(choices)
39 | }
40 | }
--------------------------------------------------------------------------------
/day2/session2/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/ConsoleGame.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.day2.session2
2 |
3 | import java.lang.Integer
4 | import akka.actor.ActorRef
5 |
6 | case class Player(name: String, gold: Integer, location: Site, items: List[GameObject], display : ActorRef) {
7 | val weapons = Nil
8 |
9 | override def toString = name + ", gold=" + gold + ", location = " + location
10 | }
11 |
12 | trait GameObject {
13 | def actions: List[GameAction]
14 |
15 | def getSiteDescription: String
16 |
17 | def getInventoryDescription: String
18 | }
19 |
20 | case class Site(name: String, description: String, var objects: List[GameObject] = Nil) {
21 | def this(name: String, description: String) = this (name, description, Nil)
22 | }
23 |
24 | trait GameAction {
25 | def update(state: GameState): (GameState, String)
26 | }
27 |
28 | class ExitAction extends GameAction {
29 | def update(state: GameState) = {
30 | System.exit(0)
31 | null
32 | }
33 |
34 | override def toString = "exit game"
35 | }
36 |
37 | case class GameMap(links: Map[Site, List[Site]]) {
38 | def update(old: Site, newsite: Site ): GameMap = GameMap(
39 | links.foldLeft(Map.empty[Site, List[Site]]) {
40 | case (m, (from, to)) => m + ((if (from == old) newsite else old) -> to.map(t => if (t == old) newsite else old))
41 | }
42 | )
43 | }
44 |
45 | object GameMap {
46 | val jail = Site("Scalata jail", "Jail in Scalata. This place reeks of justice", Nil)
47 | val townSquare = Site("Scalata town square", "Town square in Scalata, a nice village with elves or equivalent. Merchants line the streets and a small protest is forming.")
48 | val clearing = Site("Clearing by a pond", "Clearing, near a pond with deer grazing nearby. No sign of snakes, but there is a foreboding and smelly cave nearby.")
49 | val cave = Site("Cave", "Cavernous cave. A scorpion runs past you, screaming.")
50 |
51 | val initialMap = GameMap(
52 | Map(
53 | townSquare -> ( clearing :: jail :: Nil ),
54 | clearing -> ( townSquare :: cave :: Nil ),
55 | jail -> ( townSquare :: Nil ),
56 | cave -> ( clearing :: Nil )
57 | )
58 | )
59 | }
60 |
61 |
62 | //case class GameMap(jail: Site = new Site("Scalata jail", "Jail in Scalata. This place reeks of justice", new BlueKey :: Nil)) {
63 | // val townSquare = new Site("Scalata town square", "Town square in Scalata, a nice village with elves or equivalent. Merchants line the streets and a small protest is forming.")
64 | // val clearing = new Site("Clearing by a pond", "Clearing, near a pond with deer grazing nearby. No sign of snakes, but there is a foreboding and smelly cave nearby.")
65 | // val cave = new Site("Cave", "Cavernous cave. A scorpion runs past you, screaming.")
66 | // val links = Map(townSquare -> ( clearing :: jail :: Nil ),
67 | // clearing -> ( townSquare :: cave :: Nil ),
68 | // jail -> ( townSquare :: Nil ),
69 | // cave -> ( Nil )
70 | // )
71 | //}
72 |
73 | case class GameState(map: GameMap, players: Map[String,Player] = Map())
74 |
75 |
--------------------------------------------------------------------------------
/day2/session2/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/Example1LocalActor.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.day2.session2
2 |
3 | import akka.actor.Actor
4 |
5 | //Simple actor implementation with built-in-types
6 | class ConsoleActor extends Actor {
7 | def receive = {
8 | case x: String => println("Console Actor received string: " + x)
9 | case 123 => println("Wow, it's my favorite number")
10 | }
11 | }
12 |
13 | import akka.actor.Actors._
14 | object SendConsoleActor {
15 | def main(args: Array[String]) {
16 | val actor = actorOf(classOf[ConsoleActor])
17 | actor.start()
18 | actor.sendOneWay("Why hello there!")
19 | actor ! 123
20 | actor ! "Bye!"
21 | }
22 | }
--------------------------------------------------------------------------------
/day2/session2/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/Example2Messages.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.day2.session2
2 |
3 | import akka.actor.Actors._
4 | import akka.japi.Creator
5 | import akka.actor.Actor
6 | import collection.mutable.HashSet
7 |
8 | case class Attack(target: String, damage: Int)
9 |
10 | case class Login(target: String)
11 |
12 | object Example2Messages {
13 | def main(args: Array[String]) {
14 | val server = actorOf(new Creator[Actor] {
15 | def create = new Actor {
16 | val players = new HashSet[String]
17 | protected def receive = {
18 | case Login(name:String) => players.add(name)
19 | case Attack("darth vader", damage: Int) => println("the sith lord is impervious to pain")
20 | case Attack(target: String, damage: Int) => println(target + " just took " + damage + " damage.")
21 | }
22 | }
23 | })
24 | server.start()
25 |
26 | server ! Login("larry")
27 | server ! Login("darth vader")
28 |
29 | server ! Attack("larry", 123)
30 | server ! Attack("darth vader", 123)
31 | }
32 | }
--------------------------------------------------------------------------------
/day2/session2/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/Example3Callbacks.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.day2.session2
2 |
3 | import akka.actor.Actor
4 | import akka.actor.Actors._
5 | import akka.japi.Creator
6 |
7 | object Example3Callbacks {
8 | def main(args: Array[String]) {
9 | val actor = actorOf(new Creator[Actor] {
10 | def create = new Actor {
11 | protected def receive = {
12 | case 123 => self reply_? "My favorite number!"
13 | }
14 | }
15 | })
16 | actor.start()
17 | actor ! 123
18 | ( actor !! 123 ) match {
19 | case Some(x: String) => println("got response " + x)
20 | case Some(false) => println("got response false")
21 | case None => println("none")
22 | }
23 |
24 | val future = actor !!! 123
25 | val result = future
26 | println(result)
27 | }
28 | }
--------------------------------------------------------------------------------
/day2/session2/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/Example4RemoteActorSameJVM.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.day2.session2
2 |
3 | import akka.actor.Actor
4 | import akka.actor.Actors._
5 |
6 | object Example4RemoteActorSameJVM {
7 | def main(args: Array[String]) {
8 | // server code
9 | class HelloWorldActor extends Actor {
10 | def receive = {
11 | case msg => {
12 | println("hello there, message received")
13 | self reply ( msg + " World" )
14 | }
15 | }
16 | }
17 | remote.start("localhost", 9999).register("hello-service", actorOf(classOf[HelloWorldActor]))
18 |
19 | // client code
20 | val actor = remote.actorFor("hello-service", "localhost", 9999)
21 | val result = actor !! "Hello afterwards"
22 | println(result)
23 | }
24 | }
--------------------------------------------------------------------------------
/day2/session2/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/Example5RemoteActorNewJVM.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.day2.session2
2 |
3 | import akka.actor.Actor
4 | import akka.actor.Actors._
5 | import java.lang.String
6 |
7 | object Example5RemoteActorNewJVM {
8 | val service: String = "example-4-multipleJVM-server"
9 | }
10 |
11 | object Server {
12 | def main(args: Array[String]) {
13 | // server code
14 | class HelloWorldActor extends Actor {
15 | def receive = {
16 | case msg => {
17 | println("Server received message: " + msg)
18 | self reply ( msg + " World" )
19 | }
20 | }
21 | }
22 | remote.start("localhost", 9999).register(Example5RemoteActorNewJVM.service, actorOf(classOf[HelloWorldActor]))
23 | }
24 | }
25 |
26 | object Client {
27 | def main(args: Array[String]) {
28 | val actor = remote.actorFor(Example5RemoteActorNewJVM.service, "localhost", 9999)
29 | val result = actor !! "Client says hello"
30 | println("received message from server: "+result)
31 | }
32 | }
--------------------------------------------------------------------------------
/day2/session2/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/GameActor.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.frontierdevelopers.scalaworkshop.day2.session2
5 |
6 | import akka.actor.{ActorRef, Actor}
7 |
8 | sealed trait Action
9 |
10 | case class Prompt(status : String, choices : List[Choice])
11 |
12 | case class Choice(description : String, agent : ActorRef, action : Action)
13 |
14 | case class ClientChoice(player : String, action : Action)
15 |
16 | case class Join(name : String, input : ActorRef, display : ActorRef)
17 |
18 | case class Move(to : Site) extends Action
19 |
20 | case class Display(message : String) extends Action
21 |
22 | case class PickUp(thing : Item)
23 |
24 | trait Item
25 |
26 | class GameActor extends Actor {
27 | private var currentState = new GameState(GameMap.initialMap)
28 |
29 | def currentChoices(name : String) : List[Choice] = {
30 | // travelling for now
31 | val travel : List[Choice] =
32 | for (link <- currentState.map.links(currentState.players(name).location))
33 | yield Choice("Move to " + link.description, self, Move(link))
34 |
35 | val currentLocation = currentState.players(name).location
36 |
37 | val chat : List[Choice] =
38 | for ((otherName,otherPlayer) <- currentState.players.toList
39 | if otherName != name && otherPlayer.location == currentLocation)
40 | yield Choice("Chat with " + otherName, otherPlayer.display, Display(name + " says Hi!"))
41 |
42 | travel ::: chat
43 | }
44 |
45 | def notifyLocationChange(currentPlayer: Player, to: Site, notifyLeave : Boolean = false) {
46 | val name = currentPlayer.name
47 |
48 | if (notifyLeave) {
49 | currentState.players.values.filter(p => p.name != name && p.location == currentPlayer.location).foreach {
50 | _.display ! Display(name + " has left")
51 | }
52 | }
53 |
54 | currentState.players.values.filter(p => p.name != name && p.location == to).foreach {
55 | _.display ! Display(name + " has entered")
56 | }
57 | }
58 |
59 | val processAction : PartialFunction[ClientChoice,Prompt] = {
60 | case ClientChoice(name,Move(to)) => {
61 | val currentPlayer = currentState.players(name)
62 |
63 | notifyLocationChange(currentPlayer, to, true)
64 |
65 | val player = currentState.players(name).copy(location = to)
66 | currentState = currentState.copy(players = currentState.players + (name -> player))
67 | Prompt("You have moved to " + to.description, currentChoices(name))
68 | }
69 | }
70 |
71 | def receive = {
72 | case Join(name,input,display) => {
73 | val player = Player(name, 0, GameMap.townSquare, Nil, display)
74 | currentState = currentState.copy(players = currentState.players + (name -> player))
75 | notifyLocationChange(player, player.location)
76 | self.reply_?(Prompt("Welcome!\nYou are in " + player.location.description, currentChoices(name)))
77 | }
78 | case choice @ ClientChoice(player, action) if currentState.players.contains(player) =>
79 | self.reply_?(processAction(choice))
80 | }
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/day2/session2/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/GameClient.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.frontierdevelopers.scalaworkshop.day2.session2
5 |
6 | import akka.actor.{ActorRef, Actors}
7 | import annotation.tailrec
8 |
9 |
10 | object GameClient {
11 | def main (args : Array[String]) {
12 | val game = Actors.actorOf(classOf[GameActor]).start()
13 | val input = Actors.actorOf(classOf[ClientInput]).start()
14 | val display = Actors.actorOf(classOf[ClientDisplay]).start()
15 |
16 | println("What is your name?")
17 |
18 | joinGame(game, input, display, readLine())
19 | }
20 |
21 | @tailrec
22 | def joinGame (game : ActorRef, input : ActorRef, display : ActorRef, name : String) {
23 | game !! Join(name, input, display) match {
24 | case Some(p : Prompt) => input ! name; input ! p
25 | case None => {
26 | println("Time out joining game!")
27 | joinGame(game, input, display, name)
28 | }
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/day2/session3/1-lift/README:
--------------------------------------------------------------------------------
1 | Taste of Scala - Derek (Lift)
--------------------------------------------------------------------------------
/day2/session3/2-scalaz/README:
--------------------------------------------------------------------------------
1 | Taste of Scala - Kris/John (Scalaz)
--------------------------------------------------------------------------------
/day2/session3/3-scala-gui/src/main/scala/org/frontierdevelopers/scalaworkshop/gui/Example1_Button.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.gui
2 |
3 | import swing.{Button, MainFrame, SimpleSwingApplication}
4 |
5 | object Example1_Button extends SimpleSwingApplication {
6 | def top = new MainFrame {
7 | contents = new Button("Hello")
8 | }
9 | }
--------------------------------------------------------------------------------
/day2/session3/3-scala-gui/src/main/scala/org/frontierdevelopers/scalaworkshop/gui/Example2_Button.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.gui
2 |
3 | import swing.{Button, MainFrame, SimpleSwingApplication}
4 |
5 | object Example2_Button extends SimpleSwingApplication {
6 | def top = new MainFrame {
7 | contents = Button("Hello"){
8 | println("button pressed")
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/day2/session3/3-scala-gui/src/main/scala/org/frontierdevelopers/scalaworkshop/gui/Example3_Layout.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.gui
2 |
3 | import swing._
4 |
5 | object Example3_Layout extends SimpleSwingApplication {
6 | def top = new MainFrame {
7 | contents = new BoxPanel(Orientation.Vertical) {
8 | contents += new Label {text = "Hello"}
9 | contents += new Button {text = "A button"}
10 | contents += new CheckBox {text = "A checkbox"}
11 | contents += new RadioButton {text = "A radio button"}
12 | contents += new Slider
13 | contents += new TextField{text = "a text field"}
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/day2/session3/3-scala-gui/src/main/scala/org/frontierdevelopers/scalaworkshop/gui/Example4_Events.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.gui
2 |
3 | import swing._
4 | import event.{ValueChanged, ButtonClicked}
5 |
6 | object Example4_Events extends SimpleSwingApplication {
7 | def top = new MainFrame {
8 | contents = new BoxPanel(Orientation.Vertical) {
9 | val button = new Button {text = "A button"}
10 | contents += button
11 | val slider = new Slider
12 | contents += slider
13 | val textField = new TextField {text = "a text field"}
14 | contents += textField
15 | attachListeners(button, slider, textField)
16 | }
17 | }
18 |
19 | def attachListeners(button: Button, slider: Slider, textField: TextField) {
20 | listenTo(button)
21 | listenTo(slider)
22 | reactions += {
23 | case ButtonClicked(b) => textField.text = "Button pressed!"
24 | case ValueChanged(s: Slider) => textField.text = "Slider value: " + s.value
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/day2/session3/3-scala-gui/src/main/scala/org/frontierdevelopers/scalaworkshop/gui/Example5_Java2D.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.gui
2 |
3 | import scala.swing.{Component, MainFrame, SimpleSwingApplication}
4 | import swing.event._
5 | import swing.event.Key._
6 | import scala.swing.event.Key.Value
7 | import java.awt.{Font, Dimension, Graphics2D}
8 |
9 | object Example5_Java2D extends SimpleSwingApplication {
10 | def top = new MainFrame {
11 | contents = new Component {
12 | private var x = 100
13 | private var y = 100
14 |
15 | override protected def paintComponent(g: Graphics2D) {
16 | g.setFont(new Font("Lucida Sans",Font.BOLD,60))
17 | g.drawString("X", x, y)
18 | }
19 |
20 | preferredSize = new Dimension(800, 600)
21 | focusable = true
22 | listenTo(keys)
23 | reactions += {
24 | case KeyPressed(_, key, _, _) => handleKeyPress(key)
25 | }
26 |
27 | def handleKeyPress(key: Value) {
28 | key match {
29 | case Left => x = x - 10
30 | case Right => x = x + 10
31 | case Up => y = y - 10
32 | case Down => y = y + 10
33 | }
34 | repaint()
35 | }
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/day2/session3/3-scala-gui/src/main/scala/org/frontierdevelopers/scalaworkshop/gui/Example6_PopQuiz.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.gui
2 |
3 | import swing._
4 | import event.ActionEvent
5 |
6 | object Example6_PopQuiz extends SimpleSwingApplication {
7 | def top = new MainFrame {
8 | contents = new BoxPanel(Orientation.Vertical) {
9 | val checkBox = new CheckBox {text = "A checkbox"}
10 | contents += checkBox
11 | val radioButton = new RadioButton {text = "Radio button!"; selected = true}
12 | contents += radioButton
13 | listenTo(checkBox)
14 | reactions += {
15 | case ActionEvent(s) => radioButton.selected = !checkBox.selected
16 | }
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/day2/session3/README:
--------------------------------------------------------------------------------
1 | Taste of Scala - Sam (Scala + Swing)
--------------------------------------------------------------------------------
/day2/session4/README:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/day2/session4/README
--------------------------------------------------------------------------------
/derek-lift-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore emacs backup files
2 | *~
3 |
--------------------------------------------------------------------------------
/derek-lift-demo/project/build.properties:
--------------------------------------------------------------------------------
1 | #Project properties
2 | #Fri Apr 23 11:24:20 PDT 2010
3 | project.organization=Lift
4 | project.name=Lift SBT Template
5 | sbt.version=0.7.5
6 | project.version=0.1
7 | def.scala.version=2.7.7
8 | build.scala.versions=2.8.1
9 | project.initialize=false
10 |
--------------------------------------------------------------------------------
/derek-lift-demo/project/build/LiftProject.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 |
3 | class LiftProject(info: ProjectInfo) extends DefaultWebProject(info) {
4 | val liftVersion = "2.3"
5 |
6 | val akka_repo = "Akka Maven Repository" at "http://akka.io/repository"
7 | val jboss_repo = "JBoss Maven Repository" at "http://repository.jboss.org/nexus/content/groups/public/"
8 | val guiceyfruit_repo = "GuiceyFruit repo" at "http://guiceyfruit.googlecode.com/svn/repo/releases/"
9 |
10 | val akka = "se.scalablesolutions.akka" % "akka-actor" % "1.0"
11 |
12 | // uncomment the following if you want to use the snapshot repo
13 | // val scalatoolsSnapshot = ScalaToolsSnapshots
14 |
15 | // If you're using JRebel for Lift development, uncomment
16 | // this line
17 | // override def scanDirectories = Nil
18 |
19 | override def libraryDependencies = Set(
20 | "net.liftweb" %% "lift-webkit" % liftVersion % "compile",
21 | "org.mortbay.jetty" % "jetty" % "6.1.22" % "test",
22 | "junit" % "junit" % "4.5" % "test",
23 | "ch.qos.logback" % "logback-classic" % "0.9.26",
24 | "org.scala-tools.testing" %% "specs" % "1.6.6" % "test"
25 | ) ++ super.libraryDependencies
26 | }
27 |
--------------------------------------------------------------------------------
/derek-lift-demo/sbt:
--------------------------------------------------------------------------------
1 | java -Xmx512M -Xss2M -XX:+CMSClassUnloadingEnabled -jar `dirname $0`/sbt-launcher.jar "$@"
2 |
--------------------------------------------------------------------------------
/derek-lift-demo/sbt-launcher.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/derek-lift-demo/sbt-launcher.jar
--------------------------------------------------------------------------------
/derek-lift-demo/sbt.bat:
--------------------------------------------------------------------------------
1 | set SCRIPT_DIR=%~dp0
2 | java -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -jar "%SCRIPT_DIR%\sbt-launcher.jar" %*
3 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/resources/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/derek-lift-demo/src/main/resources/.keep
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/resources/props/default.props:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/derek-lift-demo/src/main/resources/props/default.props
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/bootstrap/liftweb/Boot.scala:
--------------------------------------------------------------------------------
1 | package bootstrap.liftweb
2 |
3 | import net.liftweb._
4 | import util._
5 | import Helpers._
6 |
7 | import common._
8 | import http._
9 | import sitemap._
10 | import Loc._
11 |
12 | /**
13 | * A class that's instantiated early and run. It allows the application
14 | * to modify lift's environment
15 | */
16 | class Boot {
17 | def boot {
18 | // where to search snippet
19 | LiftRules.addToPackages("code")
20 |
21 | // Build SiteMap
22 | val entries = List(
23 | Menu.i("Home") / "index", // the simple way to declare a menu
24 |
25 | // more complex because this menu allows anything in the
26 | // /static path to be visible
27 | Menu(Loc("Static", Link(List("static"), true, "/static/index"),
28 | "Static Content")))
29 |
30 | // set the sitemap. Note if you don't want access control for
31 | // each page, just comment this line out.
32 | LiftRules.setSiteMap(SiteMap(entries:_*))
33 |
34 | //Show the spinny image when an Ajax call starts
35 | LiftRules.ajaxStart =
36 | Full(() => LiftRules.jsArtifacts.show("ajax-loader").cmd)
37 |
38 | // Make the spinny image go away when it ends
39 | LiftRules.ajaxEnd =
40 | Full(() => LiftRules.jsArtifacts.hide("ajax-loader").cmd)
41 |
42 | // Use jQuery 1.4
43 | LiftRules.jsArtifacts = net.liftweb.http.js.jquery.JQuery14Artifacts
44 |
45 | // Force the request to be UTF-8
46 | LiftRules.early.append(_.setCharacterEncoding("UTF-8"))
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/code/comet/GameDisplay.scala:
--------------------------------------------------------------------------------
1 | package code.comet
2 |
3 | import net.liftweb.http.{S, SessionVar, SHtml, CometActor}
4 | import net.liftweb.http.js.{JsCmd, JsCmds}
5 | import xml._
6 | import org.frontierdevelopers.scalaworkshop.day2.session2._
7 | import akka.actor.{Actors, Actor}
8 | import net.liftweb.common.{Full, Box, Empty}
9 | import code.util.{BridgeActor, Controller}
10 |
11 | /**
12 | * This case class will contain our client state
13 | */
14 | case class ClientState(name : String, status : String, messages : List[String], choices : List[Choice])
15 |
16 | /**
17 | * This is our Comet actor. It's responsible for rendering our current state
18 | * as well as any new state returned from the GameActor.
19 | */
20 | class GameDisplay extends CometActor {
21 | // A bridge between the Lift and Akka actor libraries
22 | private val bridge = Actors.actorOf(classOf[BridgeActor]).start()
23 | bridge ! this
24 |
25 | // Make sure to stop our BridgeActor when we clean up Comet
26 | override protected def localShutdown() {
27 | bridge.stop()
28 | }
29 |
30 | /* A SessionVar represents a variable held by the
31 | * servlet Session. It provides some nice wrapper functionality,
32 | * including a default value and some transform/set operations.
33 | * Box is a Lift implementation of Option, with Empty being
34 | * the analogue of None. */
35 | object currentState extends
36 | SessionVar[Box[ClientState]](Empty)
37 |
38 | /* The render method is responsible for performing a complete render
39 | * of this component. You can also perform piecemeal updates via
40 | * the partialUpdate method (shown later). */
41 | def render = currentState.is match {
42 | case Empty => {
43 | /* We need to prompt the player for their name in order to join
44 | * the game. The ajaxForm method wraps a regular HTML form
45 | * (specified here directly with Scala's XML literals) and processes
46 | * the form as an AJAX submission. */
47 | SHtml.ajaxForm(
48 | What's your name? ++
49 | /* SHtml.text generates a text input that invokes a Scala
50 | * callback (in this case, the login method) with the text
51 | * it contains when the form is submitted. */
52 | SHtml.text("", login) ++
53 |
54 | )
55 | }
56 | case Full(state @ ClientState(name, status, messages, choices)) => {
57 | /* When we have state to render, utilize Lift's
58 | * CSS binding Domain-Specific Language (DSL) to
59 | * process the template we were given. More on CSS Bindings
60 | * can be found here:
61 | *
62 | * http://www.assembla.com/wiki/show/liftweb/Binding_via_CSS_Selectors
63 | */
64 | "#status *" #> status &
65 | "#messages *" #> messages.reverse.map{Text(_) ++ } &
66 | ".choice" #> generateChoices(state)
67 | }
68 | }
69 |
70 | /**
71 | * This defines an AJAX callback for executing a selected choice.
72 | * It returns a JsCmd, which is part of Lift's abstraction for
73 | * dealing with JavaScript.
74 | */
75 | def perform (choice : Choice)() : JsCmd = {
76 | /* The "is" accesses the current value of our SessionVar, and Box
77 | * contains a foreach (and map, etc) just like Option. */
78 | currentState.is.foreach {
79 | state =>
80 | choice.action match {
81 | // Special handling for messages
82 | case d : Display => choice.agent ! d
83 | case otherMessage => {
84 | choice.agent !! ClientChoice(state.name, choice.action) match {
85 | case Some(p : Prompt) => this ! p
86 | case other => error(other.toString)
87 | }
88 | }
89 | }
90 | }
91 | JsCmds.Noop
92 | }
93 |
94 | /**
95 | * Lift's actors, unlike Akka, define three different message handlers that are
96 | * tried in order (high, medium, low). However, like Akka, Lift uses a partial
97 | * function for the actual message handling.
98 | */
99 | override def mediumPriority = {
100 | case Display(message) => currentState.foreach {
101 | state => {
102 | currentState.set(Full(state.copy(messages = message :: state.messages)))
103 | partialUpdate(updateMessages())
104 | }
105 | }
106 | case Prompt(newStatus, message, newChoices) => currentState.foreach {
107 | state => {
108 | // Optionally prepend the provided message
109 | val newMessages = message.map(_ :: state.messages) getOrElse state.messages
110 |
111 | currentState.set(Full(state.copy(status = newStatus,
112 | messages = newMessages,
113 | choices = newChoices)))
114 | partialUpdate(updateMessages() & updateChoices())
115 | }
116 | }
117 | }
118 |
119 | /**
120 | * This defines our callback when the user submits their name
121 | * to join the game. We need to submit to the GameActor and wait
122 | * to make sure our user name was accepted. If we get any errors
123 | * we notify the user.
124 | */
125 | def login (name : String) {
126 | Controller.game !! Join(name, bridge, bridge) match {
127 | case Some(Prompt(status,message,choices)) =>
128 | currentState.set(Full(ClientState(name,status,message.map(List(_)) getOrElse Nil,choices)))
129 | case other => error("Error: " + other)
130 | }
131 | reRender()
132 | }
133 |
134 | /**
135 | * A utiltiy method to generate the JavaScript necessary to only update
136 | * the "messages" div.
137 | */
138 | def updateMessages() : JsCmd = {
139 | currentState.is.map {
140 | state => JsCmds.SetHtml("messages", state.messages.reverse.flatMap{Text(_) ++ })
141 | } openOr JsCmds.Noop
142 | }
143 |
144 | /**
145 | * A utility method to generate the JavaScript necessary to only update
146 | * the "choices" div
147 | */
148 | def updateChoices() : JsCmd = {
149 | currentState.is.map {
150 | state => JsCmds.SetHtml("choices",
151 | ("#choices ^^" #> "ignore" &
152 | ".choice" #> generateChoices(state)).apply(defaultHtml)
153 | ) &
154 | JsCmds.SetHtml("status", Text(state.status))
155 | } openOr JsCmds.Noop
156 | }
157 |
158 | /**
159 | * Because we need to generate choices in both our main render and our
160 | * updateChoices methods, we refactor out the common generation here.
161 | */
162 | def generateChoices(state : ClientState) : NodeSeq = state.choices.flatMap {
163 | /* Special handling for chat messages. Here we create a new form
164 | * that allows us to customize our message */
165 | case Choice(description, agent, Display(otherPlayer)) => {
166 | SHtml.ajaxForm(
167 |
Say "{ SHtml.text("hi", message => agent ! Display(state.name + " says: " + message)) }" to {otherPlayer}
168 |
169 | )
170 | }
171 | case choice =>
172 | SHtml.a(() => perform(choice), {choice.description} )
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/code/lib/DependencyFactory.scala:
--------------------------------------------------------------------------------
1 | package code
2 | package lib
3 |
4 | import net.liftweb._
5 | import http._
6 | import util._
7 | import common._
8 | import java.util.Date
9 |
10 | /**
11 | * A factory for generating new instances of Date. You can create
12 | * factories for each kind of thing you want to vend in your application.
13 | * An example is a payment gateway. You can change the default implementation,
14 | * or override the default implementation on a session, request or current call
15 | * stack basis.
16 | */
17 | object DependencyFactory extends Factory {
18 | implicit object time extends FactoryMaker(Helpers.now _)
19 |
20 | /**
21 | * objects in Scala are lazily created. The init()
22 | * method creates a List of all the objects. This
23 | * results in all the objects getting initialized and
24 | * registering their types with the dependency injector
25 | */
26 | private def init() {
27 | List(time)
28 | }
29 | init()
30 | }
31 |
32 | /*
33 | /**
34 | * Examples of changing the implementation
35 | */
36 | sealed abstract class Changer {
37 | def changeDefaultImplementation() {
38 | DependencyFactory.time.default.set(() => new Date())
39 | }
40 |
41 | def changeSessionImplementation() {
42 | DependencyFactory.time.session.set(() => new Date())
43 | }
44 |
45 | def changeRequestImplementation() {
46 | DependencyFactory.time.request.set(() => new Date())
47 | }
48 |
49 | def changeJustForCall(d: Date) {
50 | DependencyFactory.time.doWith(d) {
51 | // perform some calculations here
52 | }
53 | }
54 | }
55 | */
56 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/code/model/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/derek-lift-demo/src/main/scala/code/model/.keep
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/code/snippet/HelloWorld.scala:
--------------------------------------------------------------------------------
1 | package code
2 | package snippet
3 |
4 | import scala.xml.{NodeSeq, Text}
5 | import net.liftweb.util._
6 | import net.liftweb.common._
7 | import java.util.Date
8 | import code.lib._
9 | import Helpers._
10 |
11 | class HelloWorld {
12 | lazy val date: Box[Date] = DependencyFactory.inject[Date] // inject the date
13 |
14 | // replace the contents of the element with id "time" with the date
15 | def howdy = "#time *" #> date.map(_.toString)
16 |
17 | /*
18 | lazy val date: Date = DependencyFactory.time.vend // create the date via factory
19 |
20 | def howdy = "#time *" #> date.toString
21 | */
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/code/util/BridgeActor.scala:
--------------------------------------------------------------------------------
1 | package code.util
2 |
3 | import akka.actor.Actor
4 | import net.liftweb.http.CometActor
5 |
6 | /**
7 | * Because Lift has its own actor model, we need to
8 | * provide a bridge between Lift and our Akka game
9 | * engine actor. Basically this will forward messages
10 | * to a given CometActor once that CometActor has been
11 | * set.
12 | */
13 | class BridgeActor extends Actor {
14 | private var target : Option[CometActor] = None
15 | def receive = {
16 | case comet : CometActor => target = Some(comet)
17 | case msg => target.foreach(_ ! msg)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/code/util/Controller.scala:
--------------------------------------------------------------------------------
1 | package code.util
2 |
3 | import akka.actor.Actors
4 | import org.frontierdevelopers.scalaworkshop.day2.session2.GameActor
5 |
6 | /**
7 | * This is just a way to create a singleton game engine
8 | * for use by our CometDisplay clients.
9 | */
10 | object Controller {
11 | val game = Actors.actorOf(classOf[GameActor]).start()
12 | }
13 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/code/view/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/derek-lift-demo/src/main/scala/code/view/.keep
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/ClientMessages.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.day2.session2
2 |
3 | import akka.actor.ActorRef
4 |
5 | /**
6 | * This message is what the client sends to the game engine to
7 | * join into the game.
8 | *
9 | * @param name The desired name of the player
10 | * @param input An actor that we can send Prompt messages to in order
11 | * to tell the client what actions they may perform
12 | * @param display An actor that we can send Display messages to in order
13 | * to have the client display a new message
14 | */
15 | case class Join(name : String, input : ActorRef, display : ActorRef)
16 |
17 | /**
18 | * This message allows us to inform the client of a current status
19 | * and a List of possible choices based on their current state
20 | */
21 | case class Prompt(status : String, message: Option[String], choices : List[Choice])
22 |
23 | /**
24 | * This is not a message itself, but is a component of the Prompt
25 | * message. It defines a particular choice a player may make, with
26 | * a description as well as a reference to the actor that will process
27 | * the action and the action message itself that will be sent to
28 | * that actor.
29 | *
30 | * In the general case this would allow us to distribute the processing of
31 | * actions to multiple actors, but since we only have a single game engine
32 | * actor in this case, the primary purpose is to allow clients to send
33 | * messages to one another.
34 | */
35 | case class Choice(description : String, agent : ActorRef, action : Action)
36 |
37 | /**
38 | * This message is what the client sends to a particular Choice's
39 | * agent informing them of the action they want to take. Additionally,
40 | * it includes their player name so that the processing actor can
41 | * determine who is taking the action.
42 | */
43 | case class ClientChoice(player : String, action : Action)
44 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/GameActions.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.day2.session2
2 |
3 |
4 | /**
5 | * This trait will allow us to constrain what types of actions
6 | * the client can take. Because it is sealed, we can only define
7 | * client actions within This source file. Additionally,
8 | * the compiler can then deduce the exact set of actions and
9 | * warn us if we perform a pattern match on Action and miss
10 | * a case.
11 | */
12 | sealed trait Action
13 |
14 | /**
15 | * Represents movement of a given player to a new location
16 | */
17 | case class Move(to : Site) extends Action
18 |
19 |
20 | /**
21 | * Represents displaying a message to the client. In this case
22 | * we constrain it to type Action because we want to allow
23 | * the client to send messages to other clients (Chat), but we
24 | * can also use it to send async messages from the game engine
25 | * to the client.
26 | */
27 | case class Display(message : String) extends Action
28 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/GameActor.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.day2.session2
2 |
3 | import akka.actor.{ActorRef, Actor}
4 |
5 | /**
6 | * This actor is our game engine. It's responsible for
7 | * receiving messages from players, processing them, and sending
8 | * result messages back to one or more players. It's responsible
9 | * for the entire state of the game.
10 | */
11 | class GameActor extends Actor {
12 | /**
13 | * The current state of the game. The "private[this]" modifier
14 | * makes the variable private to the instance of GameActor,
15 | * not just private to instances of the GameActor class.
16 | */
17 | private[this] var currentState = new GameState(GameMap.initialMap)
18 |
19 | /**
20 | * This computes the current choices available to a given player,
21 | * identified by their name, based on a given state.
22 | */
23 | def currentChoices(name : String, state : GameState) : List[Choice] = {
24 | val currentLocation = state.players(name).location
25 |
26 | // Computes movement options based on current location.
27 | val travel : List[Choice] =
28 | for (link <- state.map.links(currentLocation))
29 | yield Choice("Move to " + link.description, self, Move(link))
30 |
31 |
32 | // Computes chat options based on which other players are in the same location
33 | val chat : List[Choice] =
34 | for ((otherName,otherPlayer) <- state.players.toList
35 | if otherName != name && otherPlayer.location == currentLocation)
36 | yield Choice("Chat with " + otherName, otherPlayer.display, Display(otherName))
37 |
38 | // The current choices are the concatenation of movement and chat choices
39 | travel ::: chat
40 | }
41 |
42 | /**
43 | * This is a utility method to encapsulate the logic of notifying other players
44 | * when a player arrives or leaves their location. The logic in this method is
45 | * defined for after the player moves since the other players' current choices
46 | * are dependent on the game state. Note that the from is optional because
47 | * when players first enter the game they essentially arrive from nowhere
48 | */
49 | def notifyLocationChange(currentPlayer: Player, from : Option[Site], to: Site, state : GameState) {
50 | val name = currentPlayer.name
51 |
52 | /* This is a local utility function we can use to send messages uniformly. By
53 | * providing two argument lists we can construct a single-argument function
54 | * based on the message and then pass that function to our foreach calls
55 | * later in the notifyLocationChange method.
56 | *
57 | * Basically what we're doing here is sending another player a new set
58 | * of choices based on our current player arriving at or leaving a
59 | * location.
60 | */
61 | def notifyOther(msg : String)(otherPlayer : Player) = {
62 | otherPlayer.input ! Prompt(otherPlayer.toString, Some(msg), currentChoices(otherPlayer.name, state))
63 | }
64 |
65 | // We first notify others in the "from" location that the player has left
66 | from.foreach {
67 | former => currentState.players.values.filter(p => p.name != name &&
68 | p.location == former).foreach(notifyOther(name + " has left"))
69 | }
70 |
71 | // Then we notify those in the "to" location that the player has arrived
72 | currentState.players.values.filter(p => p.name != name &&
73 | p.location == to).foreach(notifyOther(name + " has arrived"))
74 | }
75 |
76 | /**
77 | * We delegate to this partial function to allow the compiler to
78 | * utilize the fact that we've sealed Action. We return a tuple
79 | * of the new game state as well as an optional message
80 | * that may be sent to the client's display actor.
81 | */
82 | val processAction : PartialFunction[(Player,Action),(GameState,Option[String])] = {
83 | case (currentPlayer,Move(to)) => {
84 | val formerLocation = currentPlayer.location
85 | val newState =
86 | currentState.copy(players = currentState.players + (currentPlayer.name -> currentPlayer.copy(location = to)))
87 |
88 | notifyLocationChange(currentPlayer, Some(formerLocation), to, newState)
89 |
90 | /* A nested copy can be used to modify our player's location
91 | * and the global game state, but you can see how this could
92 | * start to get ugly if we go deeper. */
93 | (newState,
94 | Some("You are in a " + to.description))
95 | }
96 | }
97 |
98 | /**
99 | * The receive method is Akka's main actor processing loop. It's
100 | * typed as PartialFunction[Any,Unit], so we delegate to
101 | * processAction in order to get some additional compiler enforcement.
102 | */
103 | def receive = {
104 | case Join(name,input,display) => {
105 | val player = Player(name, GameMap.townSquare, display, input)
106 | currentState = currentState.copy(players = currentState.players + (name -> player))
107 | notifyLocationChange(player, None, player.location, currentState)
108 | self.reply_?(Prompt(player.toString, Some("Welcome!\nYou are in " + player.location.description), currentChoices(name,currentState)))
109 | }
110 | case choice @ ClientChoice(playerName, action) => {
111 | /* The "get" operation on the players Map returns an Option[Player],
112 | * which allows us to further chain operations (mapping via processAction)
113 | * and consolidate several actions that only occur if the player name
114 | * is actually registered. */
115 | currentState.players.get(playerName).map(processAction(_,action)).foreach {
116 | case (newState,message) => {
117 | /* At this point we know that the player exists because we're inside
118 | * the foreach */
119 | val player = newState.players(playerName)
120 |
121 | // Make sure we record the global state change
122 | currentState = newState
123 |
124 | // We can respond with the new status and choices
125 | self.reply_?(Prompt(player.toString, message, currentChoices(playerName, currentState)))
126 | }
127 | }
128 | }
129 | }
130 | }
131 |
132 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/scala/org/frontierdevelopers/scalaworkshop/day2/session2/GameState.scala:
--------------------------------------------------------------------------------
1 | package org.frontierdevelopers.scalaworkshop.day2.session2
2 |
3 | import akka.actor.ActorRef
4 |
5 | /**
6 | * This represents our (very simplified) players in the
7 | * game. We keep track of the player's name, as well as their location
8 | * and the actors for this player's client that we want to use for
9 | * communication.
10 | */
11 | case class Player(name: String, location: Site, display : ActorRef, input : ActorRef) {
12 | override def toString = name + ", location = " + location.name
13 | }
14 |
15 | /**
16 | * This represents a location in the game
17 | */
18 | case class Site(name: String, description: String)
19 |
20 | /**
21 | * This represents our map of the game via a mapping
22 | * of a given location to the other locations you may
23 | * reach from there.
24 | */
25 | case class GameMap(links: Map[Site, List[Site]])
26 |
27 | /**
28 | * This is the companion object to our GameMap case class. Here
29 | * we statically define our locations our default map for
30 | * the game.
31 | */
32 | object GameMap {
33 | val jail = Site("Scalata jail", "Jail in Scalata. This place reeks of justice")
34 | val townSquare = Site("Scalata town square", "Town square in Scalata, a nice village with elves or equivalent. Merchants line the streets and a small protest is forming.")
35 | val clearing = Site("Clearing by a pond", "Clearing, near a pond with deer grazing nearby. No sign of snakes, but there is a foreboding and smelly cave nearby.")
36 | val cave = Site("Cave", "Cavernous cave. A scorpion runs past you, screaming.")
37 |
38 | val initialMap = GameMap(
39 | Map(
40 | townSquare -> ( clearing :: jail :: Nil ),
41 | clearing -> ( townSquare :: cave :: Nil ),
42 | jail -> ( townSquare :: Nil ),
43 | cave -> ( clearing :: Nil )
44 | )
45 | )
46 | }
47 |
48 | /**
49 | * This represents the current state of our game, with
50 | * the map and a mapping of player names to players (essentially
51 | * our registry of clients).
52 | */
53 | case class GameState(map: GameMap, players: Map[String,Player] = Map())
54 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 | LiftFilter
10 | Lift Filter
11 | The Filter that intercepts lift calls
12 | net.liftweb.http.LiftFilter
13 |
14 |
15 |
16 |
17 | LiftFilter
18 | /*
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/webapp/images/ajax-loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/derek-lift-demo/src/main/webapp/images/ajax-loader.gif
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/webapp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Home
4 |
5 |
6 |
Welcome to your project!
7 |
8 |
9 | Welcome to your Lift app at Time goes here
10 |
11 |
12 |
13 |
Status: Nothing
14 |
Messages:
15 |
16 |
You may:
17 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/webapp/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Home
4 |
5 |
6 | Static content... everything you put in the /static
7 | directory will be served without additions to SiteMap
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/webapp/templates-hidden/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Lift
18 | is Copyright 2007-2011 WorldWide Conferencing, LLC.
19 | Distributed under an Apache 2.0 License.
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/main/webapp/templates-hidden/wizard-all.html:
--------------------------------------------------------------------------------
1 |
2 |
Page of
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/test/resources/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/derek-lift-demo/src/test/resources/.keep
--------------------------------------------------------------------------------
/derek-lift-demo/src/test/scala/LiftConsole.scala:
--------------------------------------------------------------------------------
1 | import bootstrap.liftweb.Boot
2 | import scala.tools.nsc.MainGenericRunner
3 |
4 | object LiftConsole {
5 | def main(args : Array[String]) {
6 | // Instantiate your project's Boot file
7 | val b = new Boot()
8 | // Boot your project
9 | b.boot
10 | // Now run the MainGenericRunner to get your repl
11 | MainGenericRunner.main(args)
12 | // After the repl exits, then exit the scala script
13 | exit(0)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/test/scala/RunWebApp.scala:
--------------------------------------------------------------------------------
1 | import org.mortbay.jetty.Connector
2 | import org.mortbay.jetty.Server
3 | import org.mortbay.jetty.webapp.WebAppContext
4 | import org.mortbay.jetty.nio._
5 |
6 | object RunWebApp extends Application {
7 | val server = new Server
8 | val scc = new SelectChannelConnector
9 | scc.setPort(8080)
10 | server.setConnectors(Array(scc))
11 |
12 | val context = new WebAppContext()
13 | context.setServer(server)
14 | context.setContextPath("/")
15 | context.setWar("src/main/webapp")
16 |
17 | server.addHandler(context)
18 |
19 | try {
20 | println(">>> STARTING EMBEDDED JETTY SERVER, PRESS ANY KEY TO STOP")
21 | server.start()
22 | while (System.in.available() == 0) {
23 | Thread.sleep(5000)
24 | }
25 | server.stop()
26 | server.join()
27 | } catch {
28 | case exc : Exception => {
29 | exc.printStackTrace()
30 | System.exit(100)
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/test/scala/code/AppTest.scala:
--------------------------------------------------------------------------------
1 | package code
2 |
3 | import java.io.File
4 | import junit.framework._
5 | import Assert._
6 | import scala.xml.XML
7 | import net.liftweb.util._
8 | import net.liftweb.common._
9 |
10 | object AppTest {
11 | def suite: Test = {
12 | val suite = new TestSuite(classOf[AppTest])
13 | suite
14 | }
15 |
16 | def main(args : Array[String]) {
17 | junit.textui.TestRunner.run(suite)
18 | }
19 | }
20 |
21 | /**
22 | * Unit test for simple App.
23 | */
24 | class AppTest extends TestCase("app") {
25 |
26 | /**
27 | * Rigourous Tests :-)
28 | */
29 | def testOK() = assertTrue(true)
30 | // def testKO() = assertTrue(false);
31 |
32 | /**
33 | * Tests to make sure the project's XML files are well-formed.
34 | *
35 | * Finds every *.html and *.xml file in src/main/webapp (and its
36 | * subdirectories) and tests to make sure they are well-formed.
37 | */
38 | def testXml() = {
39 | var failed: List[File] = Nil
40 |
41 | def handledXml(file: String) =
42 | file.endsWith(".xml")
43 |
44 | def handledXHtml(file: String) =
45 | file.endsWith(".html") || file.endsWith(".htm") || file.endsWith(".xhtml")
46 |
47 | def wellFormed(file: File) {
48 | if (file.isDirectory)
49 | for (f <- file.listFiles) wellFormed(f)
50 |
51 | if (file.isFile && handledXml(file.getName)) {
52 | try {
53 | XML.loadFile(file)
54 | } catch {
55 | case e: org.xml.sax.SAXParseException => failed = file :: failed
56 | }
57 | }
58 | if (file.isFile && handledXHtml(file.getName)) {
59 | PCDataXmlParser(new java.io.FileInputStream(file.getAbsolutePath)) match {
60 | case Full(_) => // file is ok
61 | case _ => failed = file :: failed
62 | }
63 | }
64 | }
65 |
66 | wellFormed(new File("src/main/webapp"))
67 |
68 | val numFails = failed.size
69 | if (numFails > 0) {
70 | val fileStr = if (numFails == 1) "file" else "files"
71 | val msg = "Malformed XML in " + numFails + " " + fileStr + ": " + failed.mkString(", ")
72 | println(msg)
73 | fail(msg)
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/test/scala/code/XmlSourceSpecs.scala:
--------------------------------------------------------------------------------
1 | package code
2 |
3 | import java.io.File
4 |
5 | import scala.xml.XML
6 |
7 | import org.specs.Specification
8 | import org.specs.runner.JUnit4
9 |
10 | import net.liftweb.common.Full
11 | import net.liftweb.util.PCDataXmlParser
12 |
13 | class XmlSourceSpecsTest extends JUnit4(XmlSourceSpecs)
14 |
15 | object XmlSourceSpecs extends Specification {
16 |
17 | "XML Sources" should {
18 | "be well-formed" in {
19 | /**
20 | * Tests to make sure the project's XML files are well-formed.
21 | *
22 | * Finds every *.html and *.xml file in src/main/webapp (and its
23 | * subdirectories) and tests to make sure they are well-formed.
24 | */
25 | var failed: List[File] = Nil
26 |
27 | def handledXml(file: String) =
28 | file.endsWith(".xml")
29 |
30 | def handledXHtml(file: String) =
31 | file.endsWith(".html") || file.endsWith(".htm") || file.endsWith(".xhtml")
32 |
33 | def wellFormed(file: File) {
34 | if (file.isDirectory)
35 | for (f <- file.listFiles) wellFormed(f)
36 |
37 | if (file.isFile && handledXml(file.getName)) {
38 | try {
39 | XML.loadFile(file)
40 | } catch {
41 | case e: org.xml.sax.SAXParseException => failed = file :: failed
42 | }
43 | }
44 | if (file.isFile && handledXHtml(file.getName)) {
45 | PCDataXmlParser(new java.io.FileInputStream(file.getAbsolutePath)) match {
46 | case Full(_) => // file is ok
47 | case _ => failed = file :: failed
48 | }
49 | }
50 | }
51 |
52 | wellFormed(new File("src/main/webapp"))
53 |
54 | val numFails = failed.size
55 | if (numFails > 0) {
56 | val fileStr = if (numFails == 1) "file" else "files"
57 | val msg = "Malformed XML in " + numFails + " " + fileStr + ": " + failed.mkString(", ")
58 | fail(msg)
59 | }
60 |
61 | numFails must_== 0
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/derek-lift-demo/src/test/scala/code/snippet/HelloWorldTest.scala:
--------------------------------------------------------------------------------
1 | package code
2 | package snippet
3 |
4 | import org.specs._
5 | import org.specs.runner.JUnit4
6 | import org.specs.runner.ConsoleRunner
7 | import net.liftweb._
8 | import http._
9 | import net.liftweb.util._
10 | import net.liftweb.common._
11 | import org.specs.matcher._
12 | import org.specs.specification._
13 | import Helpers._
14 | import lib._
15 |
16 |
17 | class HelloWorldTestSpecsAsTest extends JUnit4(HelloWorldTestSpecs)
18 | object HelloWorldTestSpecsRunner extends ConsoleRunner(HelloWorldTestSpecs)
19 |
20 | object HelloWorldTestSpecs extends Specification {
21 | val session = new LiftSession("", randomString(20), Empty)
22 | val stableTime = now
23 |
24 | override def executeExpectations(ex: Examples, t: =>Any): Any = {
25 | S.initIfUninitted(session) {
26 | DependencyFactory.time.doWith(stableTime) {
27 | super.executeExpectations(ex, t)
28 | }
29 | }
30 | }
31 |
32 | "HelloWorld Snippet" should {
33 | "Put the time in the node" in {
34 | val hello = new HelloWorld
35 | Thread.sleep(1000) // make sure the time changes
36 |
37 | val str = hello.howdy(Welcome to your Lift app at Time goes here ).toString
38 |
39 | str.indexOf(stableTime.toString) must be >= 0
40 | str.indexOf("Hello at") must be >= 0
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/preso/Lift_Anatomy.odg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/preso/Lift_Anatomy.odg
--------------------------------------------------------------------------------
/preso/lift-first-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/preso/lift-first-screenshot.png
--------------------------------------------------------------------------------
/sample_game/README:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samreid/scala-workshop/f62cd5f72da830bd4f39f737b9a6f4b7684e6a2b/sample_game/README
--------------------------------------------------------------------------------
/sample_game/project/build.properties:
--------------------------------------------------------------------------------
1 | #Project properties
2 | #Sun Apr 24 08:43:02 MDT 2011
3 | project.organization=dung.eon
4 | project.name=dungeon
5 | sbt.version=0.7.4
6 | project.version=1.0
7 | build.scala.versions=2.8.1
8 | project.initialize=false
9 |
--------------------------------------------------------------------------------
/sample_game/project/build/Dungeon.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 |
3 | class Dungeon(info: ProjectInfo) extends DefaultProject(info) with IdeaProject {
4 | override def mainClass = Some("dungeon.LootTheLand")
5 | val specs = "org.scala-tools.testing" %% "specs" % "1.6.7.2" % "test"
6 | val check = "org.scala-tools.testing" %% "scalacheck" % "1.8" % "test"
7 | }
8 |
9 | // vim: set ts=4 sw=4 et:
10 |
--------------------------------------------------------------------------------
/sample_game/project/plugins/Plugins.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
3 | val sbtIdeaRepo = "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
4 | val sbtIdea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.4.0"
5 | }
6 |
--------------------------------------------------------------------------------
/sample_game/src/main/scala/Dungeon.scala:
--------------------------------------------------------------------------------
1 | package dungeon
2 |
3 | import scala.annotation.tailrec
4 | import scala.util.Random
5 |
6 |
7 | // the state of our game world will be either Finished or Running, where the Running state
8 | // keeps track of everything that can change in the state of the world.
9 | sealed trait GameState
10 | case object Finished extends GameState
11 | case class Running(player: Player, playerLoc: Location, loot: Map[Location, List[Item]]) extends GameState
12 |
13 | trait Dungeon {
14 | // our main game loop
15 | @tailrec final def run(state: GameState): Unit = nextState(state) match {
16 | // if the game is finished, exit, otherwise, recurse (tail-recursively)
17 | case Finished => ()
18 | case next => run(next)
19 | }
20 |
21 | def nextState(state: GameState): GameState
22 | }
23 |
24 | object LootTheLand extends Dungeon {
25 | def main(argv: Array[String]) {
26 | print("Greetings, brave adventurer! What is your name? > ")
27 | run(
28 | // run the game from an initial state where the player's stats are randomly initialized
29 | Running(
30 | Player(readLine(), Neutral, 10 + Random.nextInt(10), 20 + Random.nextInt(20)),
31 | inn, // the player always starts in the inn
32 | Map(cave -> List(FlamingSword), forest -> List(Club)) //starting loot
33 | )
34 | )
35 | }
36 |
37 | //locations in the game - a few of these are separate vals because ther may be
38 | //loot associated with them in the starting game state, so we need to be able to
39 | //reference them directly.
40 | val inn = Location("The Inn", FriendlyPeople)
41 | val cave = Location("The Cave", Monsters)
42 | val forest = Location("The Forest", Abandoned)
43 |
44 | //generate a random, funky topology for the game world.
45 | val dungeonMap = DungeonMap.generate(
46 | inn, cave, forest,
47 | Location("The Spring", Abandoned),
48 | Location("The Mountain", Monsters)
49 | )
50 |
51 | // the nextState method is the only place in the game that I/O takes place
52 | def nextState(state: GameState): GameState = state match {
53 | case Running(player, loc, loot) =>
54 | // describe the location where the character is currently standing
55 | println(describeLocation(loc))
56 | for (items <- loot.get(loc)) {
57 | // describe anything that the character sees in the scene
58 | println(describeLoot(loot(loc)))
59 | }
60 |
61 | // get the choices that are available to the character based on the current location
62 | val currentChoices = choices(loc, loot.get(loc).toList.flatten)
63 | currentChoices.zipWithIndex.foreach {
64 | // print out the descriptions of the choices
65 | case (c, i) => println(i + ". " + c.description)
66 | }
67 |
68 | // read an int from the console and use it to select a choice.
69 | // note that to be safe, you should really not just call toInt.
70 | print("> ")
71 | currentChoices(readLine.toInt) match {
72 | // if the player chose to quit, return the Finished game state
73 | case QuitChoice => Finished
74 |
75 | // if the player chose a path, change the player's location to the location
76 | // specified by the path.
77 | case PathChoice(path) => Running(player, path.loc, loot)
78 |
79 | // if the player chose to pick up an item, update the player's inventory
80 | // and the map of game loot.
81 | case PickUpChoice(item) => Running(
82 | player.copy(inventory = item +: player.inventory),
83 | loc,
84 | removeItem(loc, item, loot)
85 | )
86 | }
87 |
88 | // this case should never be encountered
89 | case Finished => Finished
90 | }
91 |
92 | // for each path away from the location, display the direction that the path leads
93 | private def describeLocation(loc: Location) = {
94 | val paths = dungeonMap.paths(loc).map(_.direction)
95 |
96 | "You are in " + loc.name + ". There are paths leading to the " +
97 | (paths.init.mkString(", ") + " and " + paths.last) + "."
98 | }
99 |
100 | // describe any loot at the location
101 | private def describeLoot(items: List[Item]) = items match {
102 | case Nil => "There's nothing here"
103 | case i :: Nil => "You see a " + i.name + " lying on the ground."
104 | case xs => "You see " + xs.init.map(_.name).mkString(", ") + " and a " + xs.last.name + " lying on the ground."
105 | }
106 |
107 | // assemble the list of choices available to the player based upon the location and the loot map
108 | private def choices(loc: Location, loot: List[Item]) = {
109 | QuitChoice :: (loot.map(PickUpChoice) ++ dungeonMap.paths(loc).map(PathChoice)).toList
110 | }
111 |
112 | // a convenience method for updating the loot map. Looks like Loot should probably
113 | // be a class instead of just a map!
114 | private def removeItem(loc: Location, item: Item, loot: Map[Location, List[Item]]) = {
115 | loot(loc).filter(_ != item) match {
116 | case Nil => loot - loc
117 | case xs => loot + (loc -> xs)
118 | }
119 | }
120 | }
121 |
122 | // base trait for the kinds of choices that a player can make
123 | sealed trait Choice {
124 | def description: String
125 | }
126 |
127 | case object QuitChoice extends Choice {
128 | def description = "Quit"
129 | }
130 |
131 | case class PathChoice(path: Path) extends Choice {
132 | def description = "Go " + path.direction
133 | }
134 |
135 | case class PickUpChoice(item: Item) extends Choice {
136 | def description = "Pick up the " + item.name
137 | }
138 |
139 | // not used in this version of the game
140 | sealed trait Magic { def attack: Option[Int] }
141 | case class Fire(attack: Option[Int], range: Int) extends Magic
142 |
143 | // not used in this version of the game
144 | sealed trait Alignment
145 | case object Lawful extends Alignment
146 | case object Neutral extends Alignment
147 | case object Chaotic extends Alignment
148 |
149 | sealed trait Character {
150 | def alignment: Alignment
151 | def attack: Int
152 | def defense: Int
153 | }
154 |
155 | case class Player(name: String, alignment: Alignment, attack: Int, defense: Int, inventory: Inventory = new Inventory()) extends Character
156 | case class NPC(name: String, alignment: Alignment, attack: Int, defense: Int) extends Character
157 | case class Monster(species: String, alignment: Alignment, attack: Int, defense: Int) extends Character
158 |
159 | // not used in this version of the game
160 | trait EncounterGenerator {
161 | def encounter: List[Character]
162 | }
163 |
164 | // not used in this version of the game
165 | case object Abandoned extends EncounterGenerator {
166 | val encounter = Nil
167 | }
168 |
169 | // not used in this version of the game
170 | object FriendlyPeople extends EncounterGenerator {
171 | def encounter = List(
172 | NPC("The Innkeeper", Lawful, 3, 10),
173 | NPC("The Rogue", Chaotic, 6, 28)
174 | )
175 | }
176 |
177 | // not used in this version of the game
178 | object Monsters extends EncounterGenerator {
179 | def encounter = List(
180 | Monster("Goblin", Chaotic, 2, 5),
181 | Monster("Orc", Chaotic, 7, 19),
182 | Monster("Dragon", Chaotic, 25, 40)
183 | )
184 | }
185 |
186 | case class Item(
187 | name: String,
188 | cursed: Boolean,
189 | weight: Int = 1,
190 | durability: Int = 2,
191 | attack: Int = 0,
192 | magic: Option[Magic] = None,
193 | priorOwners: List[Character] = Nil
194 | )
195 |
196 | object Club extends Item("Club", false, 3, 15, 2)
197 | object FlamingSword extends Item("Flaming Sword", false, 3, 100, 10, Some(Fire(Some(30), 2)))
198 |
199 | class Inventory(val items: List[Item] = Nil) {
200 | def +(item: Item) = new Inventory(items :+ item)
201 |
202 | def +:(item: Item) = new Inventory(item :: items)
203 |
204 | def totalWeight: Int = items.map(_.weight).sum
205 |
206 | def findByName(s: String): Option[Item] = {
207 | items.find(_.name == s)
208 | }
209 |
210 | def damage(amount: Int) = {
211 | val inflict = (item: Item) => {
212 | item.copy(durability = item.durability - amount)
213 | }
214 |
215 | new Inventory(
216 | items.map(inflict).filter(_.durability <= 0)
217 | )
218 | }
219 | }
220 |
221 | case class Location(name: String, encounterGenerator: EncounterGenerator)
222 | case class Path(loc: Location, direction: String)
223 |
224 | class DungeonMap(map: Map[Location, Set[Path]]) {
225 | def paths(loc: Location): Set[Path] = map(loc)
226 | }
227 |
228 | object DungeonMap {
229 | def generate(locations: Location*): DungeonMap = {
230 | // Randomly generate a pair of directions that will be used for the "to" and "from"
231 | // instructions to get between two locations.
232 | def dirs = {
233 | val pair = if (Random.nextBoolean) ("East", "West") else ("North", "South")
234 | if (Random.nextBoolean) pair.swap else pair
235 | }
236 |
237 | // add paths between a pair of locations to the map
238 | def addPaths(a: Location, b: Location, locs: Map[Location, Set[Path]]) = {
239 | val (from, to) = dirs
240 | Map(a -> Path(b, from), b -> Path(a, to)).foldLeft(locs) {
241 | case (m, (l, e)) => m + (l -> (m.getOrElse(l, Set()) + e))
242 | }
243 | }
244 |
245 | // Randomly arrange the locations, then using a sliding window 4 elements wide,
246 | // add mutual entrances and paths between locations. The topology will likely end
247 | // up pretty funky, but that's okay.
248 | new DungeonMap(
249 | Random.shuffle(locations.toList).sliding(4).foldLeft(Map.empty[Location, Set[Path]]) {
250 | case (m, List(a, b, c, d)) => addPaths(a, b, addPaths(a, c, addPaths(a, d, m)))
251 | }
252 | )
253 | }
254 | }
255 |
256 | // vim: set ts=4 sw=4 et:
257 |
--------------------------------------------------------------------------------
/sample_game/src/test/scala/DungeonSpec.scala:
--------------------------------------------------------------------------------
1 | package dungeon
2 |
3 | import org.specs._
4 | import org.scalacheck._
5 |
6 | class DungeonSpec extends Specification {
7 | "a fireball" should {
8 | "match" in {
9 | val fireball: Magic = Fire(Some(3), 3)
10 | fireball must beLike {
11 | case Fire(attack, range) => attack must beSome(3); range must be(3)
12 | }
13 | }
14 | }
15 | }
16 |
17 | class MapSpec extends Specification with ScalaCheck {
18 | import Arbitrary.arbitrary
19 | implicit val arbLoc = Arbitrary(
20 | for (name <- Gen.alphaStr suchThat (_.size > 3))
21 | yield Location(name, Nil, Abandoned)
22 | )
23 |
24 | implicit val locsGen =
25 | for (n <- Gen.choose(10, 50); locs <- Gen.listOfN(n, arbitrary[Location]))
26 | yield locs
27 |
28 | "a randomly generated map" should {
29 | "be fully connected" in {
30 | val prop = Prop.forAllNoShrink(locsGen) {
31 | (l: List[Location]) => {
32 | val map = DungeonMap.generate(l)
33 | l.forall(
34 | loc => map.paths(loc) must exist {
35 | case dungeon.Path(to, _) => map.paths(to) must exist {
36 | case dungeon.Path(from, _) => from == loc
37 | }
38 | }
39 | )
40 | }
41 | }
42 |
43 | prop must pass
44 | }
45 | }
46 | }
47 |
48 |
49 | // vim: set ts=4 sw=4 et:
50 |
--------------------------------------------------------------------------------
/sandbox/LearnScala/.gitignore:
--------------------------------------------------------------------------------
1 | # IDEA files are generated with "sbt idea"
2 | *.iml
3 |
4 | boot/
5 | project/plugins/project/
6 | project/plugins/src_managed/
7 |
--------------------------------------------------------------------------------
/sandbox/LearnScala/project/build.properties:
--------------------------------------------------------------------------------
1 | #Project properties
2 | #Fri Apr 08 10:52:25 MDT 2011
3 | project.organization=org.frontier
4 | project.name=LearnScala
5 | sbt.version=0.7.5
6 | project.version=1.0
7 | build.scala.versions=2.8.1
8 | project.initialize=false
9 |
--------------------------------------------------------------------------------
/sandbox/LearnScala/project/build/LearnScalaProject.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 |
3 | class LearnScalaProject(info : ProjectInfo) extends DefaultProject(info) with IdeaProject {
4 | val akka_repo = "Akka Maven Repository" at "http://akka.io/repository"
5 | val jboss_repo = "JBoss Maven Repository" at "http://repository.jboss.org/nexus/content/groups/public/"
6 | val guiceyfruit_repo = "GuiceyFruit repo" at "http://guiceyfruit.googlecode.com/svn/repo/releases/"
7 |
8 | val akka = "se.scalablesolutions.akka" % "akka-actor" % "1.0"
9 | val akkaRemote = "se.scalablesolutions.akka" % "akka-remote" % "1.0"
10 | }
--------------------------------------------------------------------------------
/sandbox/LearnScala/project/plugins/Plugins.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
3 | val sbtIdeaRepo = "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
4 | val sbtIdea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.4.0"
5 | }
--------------------------------------------------------------------------------
/sandbox/LearnScala/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | [%4p] [%d{ISO8601}] [%t] %c{1}: %m%n
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/sandbox/LearnScala/src/main/scala/org/frontier/Character.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 |
5 | package org.frontier
6 |
7 | import java.io.Serializable
8 |
9 |
10 | class Character(species : String,
11 | val name : String,
12 | strength : Int = 10,
13 | val intelligence : Int = 10,
14 | health : Double = 50,
15 | attack : Double = 5,
16 | friendly : Boolean = true)
17 | extends Creature(species, strength, health, attack, friendly) with HasInventory with Serializable {
18 | override def toString = "%s (%.2f/%.2f)".format(name, health, maxHealth)
19 |
20 | override def equals(other: Any) = other match {
21 | case that : Character => this.name == that.name && this.species == that.species
22 | case _ => false
23 | }
24 |
25 | override def hashCode() = (species + ":" + name).hashCode()
26 | }
--------------------------------------------------------------------------------
/sandbox/LearnScala/src/main/scala/org/frontier/Creature.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 |
5 | package org.frontier
6 |
7 | import java.io.Serializable
8 |
9 |
10 | class Creature (val species : String,
11 | val strength : Int = 10,
12 | private var _health : Double = 50,
13 | val attack : Double = 5,
14 | val friendly : Boolean = false) extends Serializable{
15 | val maxHealth = _health
16 |
17 | def damage (amount : Double) : Unit = {
18 | health = scala.math.max(0, health - amount)
19 | }
20 |
21 | def heal(amount : Double) {
22 | health = health + amount
23 | }
24 |
25 | def heal() : Unit = heal(5) // default amount
26 |
27 | override def toString = // This is a special method
28 | "%s (%.2f/%.2f)".format(species, health, maxHealth)
29 |
30 | def health = _health
31 | def health_= (n : Double) =
32 | if (n >= 0 && n <= maxHealth) _health = n
33 | }
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/sandbox/LearnScala/src/main/scala/org/frontier/GameClient.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 |
5 | package org.frontier
6 |
7 | import annotation.tailrec
8 | import akka.actor.{ScalaActorRef, Actor, ActorRef}
9 | import Actor._
10 |
11 | object GameClient {
12 | def main (args : Array[String]) {
13 | remote.start()
14 |
15 | val (name, prompt) = makeCharacter()
16 | loopGame(name, prompt)
17 | }
18 |
19 | implicit def nameToActorRef (name : String) : ScalaActorRef =
20 | remote.actorFor(name, "localhost", 9292)
21 |
22 | val game = nameToActorRef("game")
23 |
24 | @tailrec
25 | def makeCharacter() : (String,Prompt) = {
26 | println("What is your name?")
27 |
28 | val name = readLine()
29 | (game !! Join(name)) match {
30 | case Some(prompt : Prompt) => (name,prompt)
31 | case Some(message : String) => println(message); makeCharacter()
32 | case _ => makeCharacter()
33 | }
34 | }
35 |
36 | def shutdown() {
37 | remote.shutdown
38 | exit(0)
39 | }
40 |
41 | @tailrec
42 | def loopGame(name : String, prompt : Prompt) {
43 | println(prompt.message)
44 | println("You may:")
45 |
46 | prompt.choices.zipWithIndex.foreach {
47 | case (choice,index) => println("%d. %s".format(index,choice.description))
48 | }
49 | println("x. Exit")
50 |
51 | readLine() match {
52 | case "x" => println("Goodbye!"); game ! Leave(name); shutdown(); // only here to make it tail-recursive
53 | case pick => {
54 | try {
55 | val choice = prompt.choices(pick.toInt)
56 | (choice.agent !! choice.message) match {
57 | case Some(newPrompt : Prompt) => loopGame(name, newPrompt)
58 | case Some(Info(message)) => println(message)
59 | case other => println("Invalid response from server, try again")
60 | }
61 | } catch {
62 | case ex if ex.isInstanceOf[NumberFormatException] || ex.isInstanceOf[IndexOutOfBoundsException] => {
63 | println("Invalid choice!")
64 | }
65 | }
66 | }
67 | }
68 |
69 | loopGame(name, prompt)
70 | }
71 | }
--------------------------------------------------------------------------------
/sandbox/LearnScala/src/main/scala/org/frontier/GameController.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 |
5 | package org.frontier
6 |
7 | import akka.actor.{ActorRef, Actor}
8 | import Actor._
9 |
10 | object Controllers {
11 | val game = actorOf[GameController]
12 | val inventory = actorOf[InventoryController]
13 |
14 | val gameAgent = "game"
15 | val inventoryAgent = "inventory"
16 |
17 | val registry = Map(gameAgent -> game, inventoryAgent -> inventory)
18 |
19 | registry.foreach{case (name,actor) => remote.register(name, actor)}
20 | }
21 |
22 | sealed trait Request
23 | case class Action(description : String, agent : String, message : Request)
24 | case class Prompt(message : String, choices : List[Action])
25 | case class Info(message : String)
26 |
27 | /* GameController messages */
28 | case class Join(name : String) extends Request
29 | case class Leave(name : String) extends Request
30 |
31 | class GameController extends Actor {
32 | private[this] var players = Map[String,Character]()
33 |
34 | def defaultActions (name : String) : List[Action] = {
35 | List(Action("Check inventory", Controllers.inventoryAgent, GetInventory(players(name))))
36 | }
37 |
38 | def receive = {
39 | case Join(player) => if (players.contains(player)) {
40 | self.reply_?(Info("That name is taken"))
41 | } else {
42 | players += (player -> new Character("Human", player))
43 | log.info("Player %s has joined".format(player))
44 | self.reply_?(Prompt("Not much to do now", defaultActions(player)))
45 | }
46 | case Leave(player) => {
47 | players -= player
48 | log.info("Player %s has left".format(player))
49 | self.reply_?(Info("OK"))
50 | }
51 | }
52 | }
53 |
54 |
55 |
56 | case class GetInventory(subject : HasInventory) extends Request
57 | case class AddToInventory(subject : HasInventory, item : Item) extends Request
58 | case class RemoveFromInventory(subject : HasInventory, item : Item) extends Request
59 | case class TransferItem(item : Item, from : HasInventory, to : HasInventory) extends Request
60 |
61 | class InventoryController extends Actor {
62 | private[this] var inventory = Map[HasInventory, List[Item]]().withDefaultValue(Nil)
63 | def receive = {
64 | case GetInventory(subject) => {
65 | self.reply_?(inventory(subject) match {
66 | case Nil => Info("You aren't carrying anything")
67 | case items => Info(items.mkString("You're carrying: " , ", ", ""))
68 | })
69 | }
70 | case AddToInventory(subject, item) => {
71 | val newInventory = item :: inventory(subject)
72 | inventory += (subject -> newInventory)
73 | self.reply_?(newInventory)
74 | }
75 | case TransferItem(item, from, to) => {
76 | val (matchingItems, otherItems) = inventory(from).partition(_ == item)
77 | val moved = matchingItems.headOption.map {
78 | found => inventory ++= List(from -> (matchingItems.drop(1) ::: otherItems),
79 | to -> (found :: inventory(to))); true
80 | } getOrElse false
81 |
82 | self.reply_?(moved)
83 | }
84 | }
85 | }
86 |
87 |
88 |
--------------------------------------------------------------------------------
/sandbox/LearnScala/src/main/scala/org/frontier/GameServer.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 | package org.frontier
5 |
6 |
7 | import akka.actor.Actor._
8 |
9 | object GameServer {
10 | def main (args : Array[String]) {
11 | remote.start("localhost", 9292)
12 |
13 | println("Game controller started at " + Controllers.game)
14 | println("Inventory controller started at " + Controllers.inventory)
15 | }
16 | }
--------------------------------------------------------------------------------
/sandbox/LearnScala/src/main/scala/org/frontier/Item.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 |
5 | package org.frontier
6 |
7 |
8 | trait Item {
9 | val name : String
10 | val description : String
11 | val weight : Int
12 | }
13 |
14 | //trait Inventory {
15 | // private[this] var currentInventory : List[Item] = Nil
16 | //
17 | // def inventory = currentInventory
18 | //
19 | // def add(item : Item) = synchronized {
20 | // currentInventory = item :: currentInventory
21 | // }
22 | //
23 | // def transfer (item : Item, to : Inventory) : Boolean = synchronized {
24 | // val (items, others) = currentInventory.partition(_ == item)
25 | // currentInventory = items.drop(1) ::: others
26 | // items.headOption.map { item => to.add(item); true} getOrElse false
27 | // }
28 | //}
29 |
30 | trait HasInventory
--------------------------------------------------------------------------------
/sandbox/LearnScala/src/main/scala/org/frontier/Location.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011, Derek Chen-Becker
3 | */
4 |
5 | package org.frontier
6 |
7 | trait Location extends HasInventory {
8 | val name : String
9 | val description : String
10 | }
11 |
12 | case class GameLocation (name : String, description : String) extends Location
13 |
14 | object Nowhere extends Location {
15 | val name = "Nowhere"
16 | val description = "Nothing, really"
17 | }
18 |
19 | object Expended extends Location {
20 | val name = "Used Up"
21 | val description = "Where expendable things go after they're used"
22 | }
--------------------------------------------------------------------------------
/scala-workshop.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
--------------------------------------------------------------------------------