Want to dive even deeper?

Take the course Stairway to Scala Applied Training - 3 by Bill Venners and Dick Wall and become an expert!
Stairway to Scala Applied Training - 3
by Bill Venners , Dick Wall

Check it out!
You're watching a preview of this video, click the button on the left to puchase the full version from ScalaDays 2013.
ARCHIVED

% Completed!


About

Fun Programming in Scala: Games, Algorithms, and Apps

Scala continues to be fun! Implementing algorithms to solve Sudoku puzzles, bioinformatic problems, and analyzing time-series data has been a great learning experience. Creating video games and mods have made my game time much more enjoyable and gave me an opportunity to impress my friends! To do all these, I used a myriad of technologies from Scala IDE, sbt, and giter8 to git, Android, and Play! In the process, I learned a lot more about Scala's features such as traits, iterators, streams, lazy vals, implicit classes, and typeclasses. In this talk, I will share a few games, Android apps, and algorithms that show how Scala made implementing complex programs simple. I will also demonstrate a few awesome Minecraft mods written in Scala and a "mind blowing" project.

Published on
  • 3.356
  • 18
  • 2
  • 10
  • 0
  • Fun Programming in Scala: Games, Algorithms, and Apps Shadaj Laddad youtube.com/shadajProgramming github.com/shadaj @ShadajL
  • About Me • A middle schooler • A programmer • Logo, NXT, Ruby, Python, C, and Scala! • Loves Math and Science
  • Algorithms
  • Sudoku Solving
  • Sudoku Solving github.com/shadaj/sudoku
  • Solving Sudoku (Simple)
  • Solving Sudoku (Simple) ...
  • Solving Sudoku (Simple) ...
  • Solving Sudoku (Simple) Recursion ...
  • Solving Sudoku (Smarter)
  • Solving Sudoku (Smarter) ...
  • Solving Sudoku (Smarter) Recursion ...
  • Cells object Cells { type Cell = Option[Int] implicit def intToCell(num: Int): Cell = Some(num) }
  • The Grid class val val val Grid(data: IndexedSeq[IndexedSeq[Cell]], boxWidth: Int, boxHeight: Int) { size = boxWidth*boxHeight boxesInWidth = size/boxWidth boxesInHeight = size/boxHeight def updated(x: Int, y: Int, value: Cell) = { new Grid(data.updated(y, data(y).updated(x, value)), boxWidth, boxHeight) } def apply(x: Int, y: Int) = ... def column(x: Int): IndexedSeq[Cell] = ... def row(y: Int): IndexedSeq[Cell] = ... def blockFor(x: Int, y: Int): IndexedSeq[Cell] = ... def possibleValues(x: Int, y: Int): IndexedSeq[Int] = ...
  • Solving the Puzzle def solve(data: Grid): Option[Grid] = { val filledIn = fill(data) if (filledIn.solved) { Some(filledIn) } else { guess(filledIn).view.map(solve).collectFirst{case Some(grid) => grid} } }
  • Filling in Known Values @tailrec def fill(data: Grid): Grid = { val possibleCoordinates = for (x <- 0 until data.size; y <- 0 until data.size) yield (x, y) val coordinatesToFill = possibleCoordinates.view.map { case (x, y) => (x, y, data.possibleValues(x, y)) }.collectFirst { case (x, y, possibleValues) if (possibleValues.length == 1) => (x, y, possibleValues.head) } coordinatesToFill match { case Some((x, y, value)) => { fill(data.updated(x, y, value)) } case None => data } }
  • Guessing def guess(data: Grid): Seq[Grid] = { val allCoordinates = for (x <- 0 until data.size; y <- 0 until data.size) yield (x, y) val Some((x,y)) = allCoordinates.collectFirst { case c@(x, y) if (!data(x, y).isDefined) => c } val possibilities = data.possibleValues(x, y) possibilities.map { p => data.updated(x, y, p) } }
  • Lessons Learned • Avoid mutable state, it will bite you • There is always a better way • Great improvements between the simple and smarter algorithms • Lazy can be good :D
  • GCJ and Rosalind
  • MinScalarProduct Input 2 3 1 3 -5 -2 4 1 5 1 2 3 4 5 1 0 1 0 1
  • MinScalarProduct Input 2 3 1 3 -5 -2 4 1 5 1 2 3 4 5 1 0 1 0 1 Number of test cases
  • MinScalarProduct Input 2 3 1 3 -5 -2 4 1 5 1 2 3 4 5 1 0 1 0 1 Number of test cases Items in each vector
  • MinScalarProduct Input 2 3 1 3 -5 -2 4 1 5 1 2 3 4 5 1 0 1 0 1 Number of test cases Items in each vector Vector 1 Vector 2 Test Case 1
  • MinScalarProduct Input 2 3 1 3 -5 -2 4 1 5 1 2 3 4 5 1 0 1 0 1 Number of test cases Items in each vector Vector 1 Vector 2 Test Case 1 Test Case 2
  • Output Case #1: -25 Case #2: 6
  • Solving class MinScalarTestCase(val testCaseNumber: Int, n: Int, v1: Seq[Long], v2: Seq[Long]) extends TestCase { def solve = { Solution(v1.sorted.zip(v2.sorted.reverse).map(t => t._1 * t._2).sum.toString, testCaseNumber) } } object MinScalarLauncher extends ProblemLauncher[MinScalarTestCase]("A", 1) { val converter = new TestCaseSeqConverter[MinScalarTestCase] { override def parseTestCase(lines: Iterator[String], testCaseNumber: Int) = { new MinScalarTestCase(testCaseNumber, lines.next.convert[Int], lines.next.convertSeq[Long](), lines.next.convertSeq[Long]()) } } }
  • Solving class MinScalarTestCase(val testCaseNumber: Int, n: Int, v1: Seq[Long], v2: Seq[Long]) extends TestCase { def solve = { Solution(v1.sorted.zip(v2.sorted.reverse).map(t => t._1 * t._2).sum.toString, testCaseNumber) } } object MinScalarLauncher extends ProblemLauncher[MinScalarTestCase]("A", 1) { val converter = new TestCaseSeqConverter[MinScalarTestCase] { override def parseTestCase(lines: Iterator[String], testCaseNumber: Int) = { new MinScalarTestCase(testCaseNumber, lines.next.convert[Int], lines.next.convertSeq[Long](), lines.next.convertSeq[Long]()) } } }
  • Parsing
  • Parsing github.com/shadaj/gcj-parser
  • Conversions trait Converter[T] { def convert(string: String): T }
  • Example Converters package object parser { implicit object IntConverter extends Converter[Int] { def convert(string: String) = string.toInt } ... implicit object BigIntConverter extends Converter[BigInt] { def convert(string: String) = math.BigInt(string) } ... }
  • Converting from a String implicit class ConvertingString(val string: String) extends AnyVal { def convert[T](implicit converter: Converter[T]): T = { converter.convert(string) } def convertSeq[T](tokenizer: Converter[Seq[String]] = SpaceRepeatingConverter) (implicit converter: Converter[T]): Seq[T] = { string.convert(tokenizer).map(_.convert(converter)) } }
  • Converting from a String implicit class ConvertingString(val string: String) extends AnyVal { def convert[T](implicit converter: Converter[T]): T = { converter.convert(string) } def convertSeq[T](tokenizer: Converter[Seq[String]] = SpaceRepeatingConverter) (implicit converter: Converter[T]): Seq[T] = { string.convert(tokenizer).map(_.convert(converter)) } } "1".convert[Int] "1 2 3".convertSeq[Int]() """1 2 3""".convertSeq[Int](LineRepeatingConverter)
  • Converting Test Cases trait TestCaseSeqConverter[T <: TestCase] extends Converter[Seq[T]] { def parseTestCase(lines: Iterator[String], testCaseNumber: Int): T def convert(string: String) = { val lines = Source.fromString(string).getLines lines.foldLeft((1, Seq[T]())) {case ((n, testCases), cur) => (n + 1, testCases :+ parseTestCase(Iterator.single(cur) ++ lines, n)) }._2 } }
  • Problems class Problem[T <: TestCase: TestCaseSeqConverter] { def solve(problemName: String, unneededLines: Int) { val input = Source.fromFile(problemName + ".in") val output = new PrintWriter(problemName + ".out") input.getLines.drop(unneededLines) val testCases: Seq[T] = input.parse val solutions = solve(testCases).sortBy(_.problemNumber) solutions.foreach { p => output.println("Case #" + p.problemNumber + ": " + p.answer) } output.close } def solve(testCases: Seq[T]) = { timed("Total Time: ") { testCases.map(t => timed(s"Test Case #${t.testCaseNumber} took ") { t.solve }) } } }
  • Problems class Problem[T <: TestCase: TestCaseSeqConverter] { def solve(problemName: String, unneededLines: Int) { val input = Source.fromFile(problemName + ".in") val output = new PrintWriter(problemName + ".out") input.getLines.drop(unneededLines) val testCases: Seq[T] = input.parse val solutions = solve(testCases).sortBy(_.problemNumber) solutions.foreach { p => output.println("Case #" + p.problemNumber + ": " + p.answer) } output.close } def solve(testCases: Seq[T]) = { timed("Total Time: ") { testCases.map(t => timed(s"Test Case #${t.testCaseNumber} took ") { t.solve }) } } } A test case such that there is a converter
  • Problems A test case such that there is a converter class Problem[T <: TestCase: TestCaseSeqConverter] { def solve(problemName: String, unneededLines: Int) { val input = Source.fromFile(problemName + ".in") val output = new PrintWriter(problemName + ".out") input.getLines.drop(unneededLines) which allows... def parse[T <: TestCase] (implicit converter: TestCaseSeqConverter[T]) val testCases: Seq[T] = input.parse val solutions = solve(testCases).sortBy(_.problemNumber) solutions.foreach { p => output.println("Case #" + p.problemNumber + ": " + p.answer) } output.close } def solve(testCases: Seq[T]) = { timed("Total Time: ") { testCases.map(t => timed(s"Test Case #${t.testCaseNumber} took ") { t.solve }) } } }
  • ProblemLauncher abstract class ProblemLauncher[T <: TestCase](problemName: String, unneededLines: Int = 1) { val problem: Problem[T] = new Problem[T]()(converter) val converter: TestCaseSeqConverter[T] def main(args: Array[String]) = { problem.solve(problemName, unneededLines) } }
  • Apps
  • + + + g8 shadaj/scala-android
  • WordSteal github.com/ shadaj/wordsteal
  • WordSteal github.com/ shadaj/wordsteal
  • The Activity class WordStealActivity extends TypedActivity { lazy val charactersDisplay = findView(TR.characterDisplay) lazy val input = findView(TR.input) lazy val response = findView(TR.response) lazy val checkButton = findView(TR.checkButton) lazy val assets = getAssets() lazy val en_US = Source.fromInputStream(assets.open("en_US.dic")) lazy val words = en_US.getLines.map(_.toLowerCase).toSet override def onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) setContentView(R.layout.main) newLetters ... } ... }
  • Games
  • Minecraft Mods
  • Minecraft Mods Server
  • Minecraft Mods Server Client
  • Minecraft Mods Server via Sockets Client
  • Minecraft Mods Server via Sockets Client Your Mods
  • Minecraft Mods Server via Sockets Client Your Mods g8 shadaj/scala-minecraft
  • Fill Area Mod
  • The Code val s = start.get val e = end.get for ( x <- (s.getBlockX y <- (s.getBlockY z <- (s.getBlockZ ) { val blockToEdit = min e.getBlockX) to (s.getBlockX max e.getBlockX); min e.getBlockY) to (s.getBlockY max e.getBlockY); min e.getBlockZ) to (s.getBlockZ max e.getBlockZ) world.getBlockAt(x, y, z) blockToEdit.setTypeId(id) }
  • Scala in School Spirit Day Templates
  • TechTalks
  • NeuroScala
  • NeuroScala github.com/shadaj/neuro-thinkgear
  • How it Works
  • How it Works
  • How it Works
  • How it Works
  • How it Works
  • How it Works
  • How it Works Your Programs
  • The NeuroData types class NeuroData case class EEGPower(delta: Int, theta: Int, lowAlpha: Int, highAlpha: Int, lowBeta: Int, highBeta: Int, lowGamma: Int, highGamma: Int) extends NeuroData case class ESense(attention: Int, meditation: Int) extends NeuroData case class PoorSignalLevel(level: Int) extends NeuroData case class EEG(sense: ESense, power: EEGPower, poorSignalLevel: PoorSignalLevel) extends NeuroData case class Blink(power: Int) extends NeuroData case class RawData(level: Int) extends NeuroData
  • The Core - NeuroIterator class NeuroIterator(rawOutput: Boolean = false, jsonFormat: Boolean = true, host: String = "127.0.0.1", port: Int = 13854) extends Iterator[NeuroData] { val neuroSocket = new Socket(host, port) val neuroInput = new BufferedReader(new InputStreamReader(neuroSocket.getInputStream)) val neuroOutput = new OutputStreamWriter(neuroSocket.getOutputStream) def configure(rawOutput: Boolean = false, jsonFormat: Boolean = true) ... configure(rawOutput, jsonFormat) def hasNext = true def next = { neuroInput.readLine } } Implicit conversions!
  • Demo - MindSnakey
  • Demo - MindSnakey github.com/shadaj/mind-snakey
  • The Setup NeuroIterator NeuroData SnakeyActor Tick Timer D ,D raw wn ra ScreenActor Dra w, Mo Draw ve, n, T ic Tur k, n, ... SnakeActor
  • Sending NeuroData class NeuroSender extends Thread { ... val snakeyActor = host.system.actorFor("/user/snakeyActor") private lazy val in: NeuroIterator = { Try(new NeuroIterator) match { case Success(v) => { ... v } case Failure(e) => { ... in } } } override def run { while (!waitingForStop) { snakeyActor ! in.next } in.neuroSocket.close() } }
  • Sending Ticks and Draws object FrameRateUpdate extends ActionListener { def actionPerformed(event: ActionEvent) { snakeyActor ! Tick screenActor ! Draw } } val timer = new Timer(FRAME_RATE, FrameRateUpdate) timer.start()
  • The FSM (Finite State Machine) def running : Receive = { ... case StopGame => { ... context.become(stopped orElse common) } } def stopped : Receive = { case StartGame => { ... context.become(running orElse common) } } def common : Receive = ... def receive = stopped orElse common
  • Detecting which direction to turn case Tick => { snakeActor ! CheckEating(fruit, self) if (!waitingForBlink) { snakeActor ! Move } if (timeSinceBlink > DOUBLEBLINK && lastBlink != 0) { snakeActor ! TurnRight lastBlink = 0 } snakeActor ! Tick snakeActor ! CheckBadBlocks(badBlocks, self) } case Blink(power: Int) if power >= MIN_BLINK_POWER => { if (timeSinceBlink <= DOUBLEBLINK) { snakeActor ! TurnLeft lastBlink = 0 } else { lastBlink = System.currentTimeMillis } }
  • What’s Next
  • What’s Next
  • What’s Next
  • Follow me online! • youtube.com/shadajProgramming • github.com/shadaj • twitter.com/ShadajL
  • A L ! A S C E S L U R

Comments

Nicholas Sterling 2 years ago
What an amazing 13-year-old! In 15 years he'll be writing Scala's successor...
The kid is just brilliant!!

Login to add comments!