Created
May 1, 2011 19:40
-
-
Save d-smith/950796 to your computer and use it in GitHub Desktop.
Game Sample - Learn Scala in 2 Days
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Item { | |
val weight: Int | |
val description: String | |
val name: String | |
} | |
class Action | |
case class Pickup(item: Option[Item]) extends Action | |
case class Drop(item: Option[Item]) extends Action | |
case class Move(to: Location) extends Action | |
case class Unlock extends Action | |
abstract class Armor(protection: Int) extends Item | |
case class SimpleArmor(name: String, protection: Int, weight: Int) extends Armor(protection) { | |
val description = "SImple armor" | |
} | |
case class Person(name: String, health: Int, inventory: Inventory) { | |
def pickup(item: Item) : Person = { | |
copy(inventory = inventory.add(item)) | |
} | |
def drop(item: Item) : Person = { | |
copy(inventory = inventory.remove(item)) | |
} | |
} | |
case class Path(action: String, from: Location, to: Location) | |
case class Location(name: String, description: String, items: List[Item]) | |
case class GameMap(locations: Set[Location], paths: Set[Path]) { | |
val currentLocation = locations.headOption | |
def addLocation(location: Location): GameMap = { | |
copy(locations + location) | |
} | |
def addPath(path: Path): GameMap = { | |
copy(paths = paths + path) | |
} | |
def findPath(fromLocation: Location, toLocation: Location): Option[Path] = { | |
val path = paths.collect { | |
case p: Path if p.from == fromLocation && p.to == toLocation => p | |
} | |
path.headOption | |
} | |
def findMoves(current: Location): Set[Path] = { | |
val moves = paths.collect { | |
case p: Path if p.from == current => p | |
} | |
moves | |
} | |
} | |
object GameMap { | |
def findLocation(name: String, locations: Set[Location]): Location = { | |
val loc = locations.collect { | |
case l: Location if l.name == name => l | |
} | |
val found = loc.headOption | |
found match { | |
case None => new Location("limbo", "limbo", List[Item]()) | |
case _ => found.head | |
} | |
} | |
//Use the apply method in the companion object to build the initial map - doesn't really | |
//belong here... | |
def apply() = { | |
val initialMap = | |
<gamemap> | |
<locations> | |
<location name="dungeon" desc="ye ancient dungeon"/> | |
<location name="subdungeon" desc="dankest pit"/> | |
<location name="coffee bar" desc="coffee awaits"/> | |
</locations> | |
<paths> | |
<path from="dungeon" to="subdungeon" action="descend to the dungeon's dungeon"/> | |
<path from="subdungeon" to="dungeon" action="ascend to the dungeon"/> | |
<path from="dungeon" to="coffee bar" action="coffee break"/> | |
<path from="coffee bar" to="dungeon" action="back to torture"/> | |
</paths> | |
</gamemap> | |
val locNodes = initialMap \\ "location" | |
val theLocations = locNodes.map { | |
case n: xml.Node => new Location(n \ "@name" text, | |
n \ "@desc" text, List[Item]()) | |
}.toSet | |
val pathNodes = initialMap \\ "path" | |
val thePaths = pathNodes.map { | |
case n: xml.Node => new Path(n \ "@action" text, | |
findLocation(n \ "@from" text, theLocations.toSet), | |
findLocation(n \ "@to" text, theLocations)) | |
}.toSet | |
new GameMap(theLocations, thePaths) | |
} | |
} | |
case class GameState(person: Person, location: Location, gameMap: GameMap){ | |
def findMoves() : Set[Path] = { | |
gameMap.findMoves(location) | |
} | |
} | |
object GameHandlers { | |
val GameHandlerPickup: PartialFunction[(GameState,Action),GameState] = { | |
case(gameState @ GameState(person, _,_), Pickup(Some(item))) => | |
gameState.copy(person = person.pickup(item)) | |
} | |
val GameHandlerDrop: PartialFunction[(GameState,Action),GameState] = { | |
case(gameState @ GameState(person, _,_), Drop(Some(item))) => | |
gameState.copy(person = person.drop(item)) | |
} | |
val GameHandlerMove: PartialFunction[(GameState,Action),GameState] = { | |
case(gameState @ GameState(_, location, gameMap), Move(to)) => { | |
val path = gameState.gameMap.findPath(location, to) | |
path match { | |
case None => gameState | |
case _ => gameState.copy(location = to) | |
} | |
} | |
} | |
val GameHandlerAll = GameHandlerPickup.orElse(GameHandlerDrop.orElse(GameHandlerMove)) | |
} | |
case class Inventory(items: Set[Item]) { | |
def add(item: Item) : Inventory = { | |
new Inventory(items + item) | |
} | |
def remove(item: Item) : Inventory = { | |
new Inventory(items - item) | |
} | |
//partial function | |
def weapons: Set[Weapon] = items.collect { | |
case weapon: Weapon => weapon | |
} | |
def findWeapon(): Option[Item] = { | |
weapons.headOption | |
} | |
} | |
abstract class Weapon(damage: Int, durability: Int) extends Item | |
case class SimpleSword(name: String, length: Int, durability: Int, damage: Int, | |
weight: Int) extends Weapon(damage, durability ) { | |
val description = "Simple sword" | |
def actions() : Set[String] = { | |
Set("swing", "chop", "stap","clean") | |
} | |
} | |
object DungeonGame { | |
//Main game loop - verified this is tail recursive | |
def gameloop(gs: GameState): GameState = { | |
println("You are in " + gs.location) | |
val moves = gs.findMoves() | |
println("You can move...") | |
for ((move, i) <- moves.zipWithIndex) { | |
println(i + ": " + move.action) | |
} | |
print("Tell me your move: ") | |
val response: Int = try { | |
readLine.toInt | |
} catch { | |
case _ => -1 | |
} | |
val movesArray = moves.toArray | |
if (response == -1 || response >= movesArray.size) { | |
println("Concentrate and try again") | |
gameloop(gs) | |
} else { | |
val pick = movesArray(response.toInt) | |
val action = new Move(pick.to) | |
gameloop(GameHandlers.GameHandlerAll.apply(gs, action)) | |
} | |
} | |
def main(args: Array[String]) = { | |
println("DungeonMan! Are you enquiring about an challenge?") | |
val gameMap = GameMap() | |
val peasant = new Person("peon", 1, Inventory(Set())) | |
val gs = new GameState(peasant, gameMap.locations.head, gameMap) | |
gameloop(gs) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment