├── .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 |
    18 |
      19 |
    • Something
    • 20 |
    21 |
    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 | <span class="lift:Menu.title">code:app:0.1-SNAPSHOT</span> 7 |