Rewrite the block system so that I can implement the catamorphism easily
This commit is contained in:
parent
33df0eb120
commit
8b52321920
11 changed files with 240 additions and 57 deletions
|
@ -20,9 +20,12 @@ lazy val graph = (project in file("graph")).settings(
|
|||
name := "fancadegraph",
|
||||
version := "0.1.0",
|
||||
scalaVersion := "2.13.3",
|
||||
resolvers += Resolver.bintrayRepo("alexknvl", "maven"),
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scala-graph" %% "graph-core" % "1.13.2",
|
||||
"com.alexknvl" %% "polymorphic" % "0.5.0",
|
||||
),
|
||||
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.0" cross CrossVersion.full),
|
||||
).dependsOn(scodec)
|
||||
|
||||
lazy val tagless = (project in file("tagless")).settings(
|
||||
|
@ -36,6 +39,7 @@ lazy val tagless = (project in file("tagless")).settings(
|
|||
"org.typelevel" %% "cats-core" % "2.1.1",
|
||||
"org.typelevel" %% "cats-effect" % "2.1.3",
|
||||
"io.chrisdavenport" %% "fuuid" % "0.4.0",
|
||||
"io.higherkindness" %% "droste-core" % "0.8.0",
|
||||
"org.scalameta" %% "munit" % "0.7.9" % Test,
|
||||
),
|
||||
testFrameworks += new TestFramework("munit.Framework"),
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
package tf.bug.fancadegraph
|
||||
|
||||
import polymorphic._
|
||||
import tf.bug.fancadescodec.{Metadata, Position}
|
||||
|
||||
case class Block[T](
|
||||
case class Block(
|
||||
position: Position,
|
||||
definition: BlockDefinition[T],
|
||||
argument: T
|
||||
)(
|
||||
implicit ev: Argument[T]
|
||||
template: Exists[Template]
|
||||
) {
|
||||
def metadata: Vector[Metadata] = ev.encode(argument)(position)
|
||||
}
|
||||
|
||||
object Block {
|
||||
def apply(position: Position, definition: BlockDefinition[Unit]): Block[Unit] =
|
||||
new Block(
|
||||
position,
|
||||
definition,
|
||||
()
|
||||
)
|
||||
def metadata: Vector[Metadata] = {
|
||||
val t = Exists.unwrap(template)
|
||||
t.ev.encode(t.args)(position)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
package tf.bug.fancadegraph
|
||||
|
||||
import cats.data._
|
||||
import polymorphic._
|
||||
import scalax.collection.Graph
|
||||
import scalax.collection.GraphEdge.DiHyperEdge
|
||||
import tf.bug.{fancadescodec => fansc}
|
||||
|
||||
case class Level(
|
||||
blocks: Set[Block[_]],
|
||||
blocks: Set[Block],
|
||||
wires: Graph[Pin, DiHyperEdge]
|
||||
)
|
||||
) {
|
||||
def boundary =
|
||||
blocks.foldLeft(fansc.Boundary(0, 0, 0)) {
|
||||
case (fansc.Boundary(lr, du, nf), block) =>
|
||||
val tlr = block.position.lr + Exists.unwrap(block.template).definition.width
|
||||
val tdu = block.position.du + Exists.unwrap(block.template).definition.height
|
||||
val tnf = block.position.nf + Exists.unwrap(block.template).definition.depth
|
||||
fansc.Boundary(lr.max(tlr), du.max(tdu), nf.max(tnf))
|
||||
}
|
||||
}
|
||||
|
||||
object Level {
|
||||
|
||||
|
@ -17,26 +27,21 @@ object Level {
|
|||
}
|
||||
|
||||
def encode(level: Level): tf.bug.fancadescodec.Entry = {
|
||||
val boundary = level.blocks.foldLeft(fansc.Boundary(0, 0, 0)) {
|
||||
case (fansc.Boundary(lr, du, nf), block) =>
|
||||
val tlr = block.position.lr + block.definition.width
|
||||
val tdu = block.position.du + block.definition.height
|
||||
val tnf = block.position.nf + block.definition.depth
|
||||
fansc.Boundary(lr.max(tlr), du.max(tdu), nf.max(tnf))
|
||||
}
|
||||
val boundary = level.boundary
|
||||
val objs: Vector[fansc.Obj] = {
|
||||
val initialVector = Vector.fill(boundary.sizeNF, boundary.sizeDU, boundary.sizeLR)(fansc.Obj.empty)
|
||||
level.blocks.foldLeft(initialVector) {
|
||||
case (ew, nb) =>
|
||||
val fansc.Position(sx, sy, sz) = nb.position
|
||||
(0 until nb.definition.depth).foldLeft(ew) {
|
||||
val nbd = Exists.unwrap(nb.template).definition
|
||||
(0 until nbd.depth).foldLeft(ew) {
|
||||
case (zw, z) =>
|
||||
(0 until nb.definition.height).foldLeft(zw) {
|
||||
(0 until nbd.height).foldLeft(zw) {
|
||||
case (yw, y) =>
|
||||
(0 until nb.definition.width).foldLeft(yw) {
|
||||
(0 until nbd.width).foldLeft(yw) {
|
||||
case (xw, x) =>
|
||||
val objInd = x + (y * nb.definition.width) + (z * nb.definition.width * nb.definition.height)
|
||||
xw.updated(sz + z, xw(sz + z).updated(sy + y, xw(sz + z)(sy + y).updated(sx + x, nb.definition.ids(objInd))))
|
||||
val objInd = x + (y * nbd.width) + (z * nbd.width * nbd.height)
|
||||
xw.updated(sz + z, xw(sz + z).updated(sy + y, xw(sz + z)(sy + y).updated(sx + x, nbd.ids(objInd))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package tf.bug.fancadegraph
|
|||
import cats.effect.{Blocker, ExitCode, IO, IOApp}
|
||||
import fs2._
|
||||
import java.nio.file.{Paths, StandardOpenOption}
|
||||
import polymorphic._
|
||||
import scalax.collection.Graph
|
||||
import scalax.collection.GraphEdge.DiHyperEdge
|
||||
import scodec.stream.StreamEncoder
|
||||
|
@ -11,8 +12,8 @@ import tf.bug.fancadescodec.{Cartridge, Codecs, Position}
|
|||
object Main extends IOApp {
|
||||
|
||||
override def run(args: List[String]): IO[ExitCode] = {
|
||||
val winBlock = Block(Position(0, 0, 0), BlockDefinition.Win, true)
|
||||
val loseBlock = Block(Position(0, 0, 3), BlockDefinition.Lose, true)
|
||||
val winBlock = Block(Position(0, 0, 0), Exists(Template(BlockDefinition.Win, true)))
|
||||
val loseBlock = Block(Position(0, 0, 3), Exists(Template(BlockDefinition.Lose, true)))
|
||||
val loseAfter = Pin(loseBlock, BlockDefinition.Lose.after)
|
||||
val winBefore = Pin(winBlock, BlockDefinition.Win.before)
|
||||
val level = Level(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package tf.bug.fancadegraph
|
||||
|
||||
case class Pin(
|
||||
owner: Block[_],
|
||||
owner: Block,
|
||||
definition: PinDefinition
|
||||
)
|
||||
|
|
3
graph/src/main/scala/tf/bug/fancadegraph/Template.scala
Normal file
3
graph/src/main/scala/tf/bug/fancadegraph/Template.scala
Normal file
|
@ -0,0 +1,3 @@
|
|||
package tf.bug.fancadegraph
|
||||
|
||||
case class Template[T](definition: BlockDefinition[T], args: T)(implicit val ev: Argument[T])
|
|
@ -1,35 +1,56 @@
|
|||
package tf.bug.fancadetagless
|
||||
|
||||
import tf.bug.fancadegraph.Level
|
||||
import tf.bug.fancadegraph.BlockDefinition
|
||||
import cats._
|
||||
import cats.implicits._
|
||||
import higherkindness.droste._
|
||||
import polymorphic._
|
||||
import tf.bug.fancadegraph.PinDefinition
|
||||
import tf.bug.fancadegraph.Level
|
||||
import tf.bug.fancadegraph.Block
|
||||
import tf.bug.fancadegraph.Template
|
||||
import tf.bug.fancadescodec.Position
|
||||
import scalax.collection.Graph
|
||||
import tf.bug.fancadegraph.Argument
|
||||
|
||||
sealed trait Fancade {
|
||||
sealed trait Fancade[A] {
|
||||
val template: Exists[Template]
|
||||
val pins: Vector[PinDefinition]
|
||||
|
||||
def render: Level
|
||||
}
|
||||
|
||||
object Fancade {
|
||||
|
||||
final case class Capture[T](newBlock: BlockDefinition[T], args: T, pins: Vector[PinDefinition])(implicit arg: Argument[T]) extends Fancade {
|
||||
override def render: Level = {
|
||||
Level(
|
||||
Set(
|
||||
Block(
|
||||
Position(0, 0, 0),
|
||||
newBlock,
|
||||
args
|
||||
)
|
||||
),
|
||||
Graph.empty
|
||||
)
|
||||
}
|
||||
implicit val fancadeFunctor: Functor[Fancade] = new Functor[Fancade] {
|
||||
override def map[A, B](fa: Fancade[A])(f: A => B): Fancade[B] =
|
||||
fa match {
|
||||
case Capture(template, pins) =>
|
||||
Capture(template, pins)
|
||||
case Use(children, template, inputs, pins) =>
|
||||
Use(children.map(f), template, inputs, pins)
|
||||
}
|
||||
}
|
||||
|
||||
case class Capture[A](template: Exists[Template], pins: Vector[PinDefinition]) extends Fancade[A]
|
||||
case class Use[A](children: Vector[A], template: Exists[Template], inputs: Vector[PinDefinition], pins: Vector[PinDefinition]) extends Fancade[A]
|
||||
|
||||
def flatten: Algebra[Fancade, PointerChain[Fancade]] = Algebra {
|
||||
case Capture(template, pins) =>
|
||||
PointerChain.single(Capture(template, pins))
|
||||
case Use(children, template, inputs, pins) =>
|
||||
???
|
||||
}
|
||||
val stack: FancadeF => PointerChain[Fancade] = scheme.cata(flatten)
|
||||
def render(f: FancadeF): Level = {
|
||||
val pointerChain = stack(f)
|
||||
val blocks = pointerChain.map {
|
||||
case (ind, fc) =>
|
||||
Block(
|
||||
Position(0, ind, 0),
|
||||
fc.template
|
||||
)
|
||||
}
|
||||
|
||||
Level(
|
||||
blocks.toSet,
|
||||
???
|
||||
)
|
||||
}
|
||||
final case class Use(inputs: Vector[Fancade])
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package tf.bug.fancadetagless
|
||||
|
||||
import higherkindness.droste.data._
|
||||
import polymorphic._
|
||||
import tf.bug.fancadetagless.Fanscript.{Position, ScreenSize}
|
||||
import tf.bug.fancadescodec.Metadata.Sample
|
||||
import tf.bug.fancadegraph.BlockDefinition
|
||||
import tf.bug.fancadegraph.Template
|
||||
|
||||
trait Fanscript[F[_]] {
|
||||
|
||||
|
@ -25,7 +30,7 @@ trait Fanscript[F[_]] {
|
|||
def createObj(original: F[Obj]): F[Obj]
|
||||
def destroyObj(obj: F[Obj]): F[Obj]
|
||||
|
||||
def playSound(loop: Boolean, sound: Nothing)(volume: F[Float], pitch: F[Float]): F[Float]
|
||||
def playSound(loop: Boolean, sound: Sample)(volume: F[Float], pitch: F[Float]): F[Float]
|
||||
def stopSound(channel: F[Float]): F[Unit]
|
||||
def volumePitch(channel: F[Float], volume: F[Float], pitch: F[Float]): F[Unit]
|
||||
|
||||
|
@ -70,6 +75,92 @@ trait Fanscript[F[_]] {
|
|||
|
||||
object Fanscript {
|
||||
|
||||
trait Program[R] {
|
||||
def run[F[_]](implicit interp: Fanscript[F]): F[R]
|
||||
}
|
||||
|
||||
implicit object fancade extends Fanscript[FancadeW] {
|
||||
|
||||
override def lift(value: Float): FancadeW[Float] =
|
||||
Fix(Fancade.Capture(
|
||||
Exists(Template(
|
||||
BlockDefinition.NumberValue,
|
||||
value
|
||||
)),
|
||||
Vector(BlockDefinition.NumberValue.output)
|
||||
))
|
||||
override def lift(value: Vector3): FancadeW[Vector3] = ???
|
||||
override def lift(value: Rotation): FancadeW[Rotation] = ???
|
||||
override def lift(value: Boolean): FancadeW[Boolean] = ???
|
||||
|
||||
override def win(stop: Boolean): FancadeW[Unit] = ???
|
||||
override def lose(stop: Boolean): FancadeW[Unit] = ???
|
||||
override def setScore(score: FancadeW[Float]): FancadeW[Unit] = ???
|
||||
override def setCamera(position: FancadeW[Vector3], rotation: FancadeW[Rotation], distance: FancadeW[Float]): FancadeW[Unit] = ???
|
||||
override def setLight(position: FancadeW[Vector3], rotation: FancadeW[Rotation]): FancadeW[Unit] = ???
|
||||
override def screenSize: FancadeW[ScreenSize] = ???
|
||||
override def accelerometer: FancadeW[Vector3] = ???
|
||||
|
||||
override def getPosition(obj: FancadeW[Obj]): FancadeW[Position] =
|
||||
Fix(Fancade.Use(
|
||||
Vector(obj),
|
||||
Exists(Template(
|
||||
BlockDefinition.GetPosition,
|
||||
()
|
||||
)),
|
||||
Vector(BlockDefinition.GetPosition.obj),
|
||||
Vector(BlockDefinition.GetPosition.position)
|
||||
))
|
||||
override def setPosition(obj: FancadeW[Obj], position: FancadeW[Vector3], rotation: FancadeW[Rotation]): FancadeW[Unit] = ???
|
||||
override def raycast(from: FancadeW[Vector3], to: FancadeW[Vector3]): FancadeW[Raycast] = ???
|
||||
override def getSize(obj: FancadeW[Obj]): FancadeW[Size] = ???
|
||||
override def setVisible(obj: FancadeW[Obj], visible: FancadeW[Boolean]): FancadeW[Unit] = ???
|
||||
override def createObj(original: FancadeW[Obj]): FancadeW[Obj] = ???
|
||||
override def destroyObj(obj: FancadeW[Obj]): FancadeW[Obj] = ???
|
||||
|
||||
override def playSound(loop: Boolean, sound: Sample)(volume: FancadeW[Float], pitch: FancadeW[Float]): FancadeW[Float] = ???
|
||||
override def stopSound(channel: FancadeW[Float]): FancadeW[Unit] = ???
|
||||
override def volumePitch(channel: FancadeW[Float], volume: FancadeW[Float], pitch: FancadeW[Float]): FancadeW[Unit] = ???
|
||||
|
||||
override def addForce(obj: FancadeW[Obj], force: FancadeW[Vector3], applyAt: FancadeW[Vector3], torque: FancadeW[Vector3]): FancadeW[Unit] = ???
|
||||
override def getVelocity(obj: FancadeW[Obj]): FancadeW[Velocity] = ???
|
||||
override def setVelocity(obj: FancadeW[Obj], velocity: FancadeW[Vector3], spin: FancadeW[Vector3]): FancadeW[Unit] = ???
|
||||
override def setLocked(obj: FancadeW[Obj], position: FancadeW[Vector3], rotation: FancadeW[Vector3]): FancadeW[Unit] = ???
|
||||
override def setMass(obj: FancadeW[Obj], mass: FancadeW[Float]): FancadeW[Unit] = ???
|
||||
override def setFriction(obj: FancadeW[Obj], friction: FancadeW[Float]): FancadeW[Unit] = ???
|
||||
override def setBounciness(obj: FancadeW[Obj], bounciness: FancadeW[Float]): FancadeW[Unit] = ???
|
||||
override def setGravity(gravity: FancadeW[Vector3]): FancadeW[Unit] = ???
|
||||
override def addConstraint(base: FancadeW[Obj], part: FancadeW[Obj], pivot: FancadeW[Vector3]): FancadeW[Constraint] = ???
|
||||
override def linearLimits(constraint: FancadeW[Constraint], lower: FancadeW[Vector3], upper: FancadeW[Vector3]): FancadeW[Unit] = ???
|
||||
override def angularLimits(constraint: FancadeW[Constraint], lower: FancadeW[Vector3], upper: FancadeW[Vector3]): FancadeW[Unit] = ???
|
||||
override def linearSpring(constraint: FancadeW[Constraint], stiffness: FancadeW[Vector3], damping: FancadeW[Vector3]): FancadeW[Unit] = ???
|
||||
override def angularSpring(constraint: FancadeW[Constraint], stiffness: FancadeW[Vector3], damping: FancadeW[Vector3]): FancadeW[Unit] = ???
|
||||
override def linearMotor(constraint: FancadeW[Constraint], speed: FancadeW[Vector3], force: FancadeW[Vector3]): FancadeW[Unit] = ???
|
||||
override def angularMotor(constraint: FancadeW[Constraint], speed: FancadeW[Vector3], force: FancadeW[Vector3]): FancadeW[Unit] = ???
|
||||
|
||||
override def ifElse(cond: FancadeW[Boolean])(ifTrue: FancadeW[Unit])(ifFalse: FancadeW[Unit]): FancadeW[Unit] = ???
|
||||
override def onPlay(onPlay: FancadeW[Unit]): FancadeW[Unit] = ???
|
||||
override def onBoxArt(onBoxArt: FancadeW[Unit]): FancadeW[Unit] = ???
|
||||
override def onTouch(onTouch: FancadeW[Touch] => FancadeW[Unit]): FancadeW[Unit] = ???
|
||||
|
||||
override def screenSizeWidth(screenSize: FancadeW[ScreenSize]): FancadeW[Float] = ???
|
||||
override def screenSizeHeight(screenSize: FancadeW[ScreenSize]): FancadeW[Float] = ???
|
||||
|
||||
override def positionPosition(position: FancadeW[Position]): FancadeW[Vector3] = ???
|
||||
override def positionRotation(position: FancadeW[Position]): FancadeW[Rotation] = ???
|
||||
|
||||
override def raycastHit(raycast: FancadeW[Raycast]): FancadeW[Boolean] = ???
|
||||
override def raycastPosition(raycast: FancadeW[Raycast]): FancadeW[Vector3] = ???
|
||||
override def raycastObj(raycast: FancadeW[Raycast]): FancadeW[Obj] = ???
|
||||
|
||||
override def sizeMin(size: FancadeW[Size]): FancadeW[Vector3] = ???
|
||||
override def sizeMax(size: FancadeW[Size]): FancadeW[Vector3] = ???
|
||||
|
||||
override def velocityVelocity(velocity: FancadeW[Velocity]): FancadeW[Vector3] = ???
|
||||
override def velocitySpin(velocity: FancadeW[Velocity]): FancadeW[Vector3] = ???
|
||||
|
||||
}
|
||||
|
||||
case class ScreenSize(width: Float, height: Float)
|
||||
case class Position(position: Vector3, rotation: Rotation)
|
||||
case class Raycast(hit: Boolean, position: Vector3, obj: Obj)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package tf.bug.fancadetagless
|
||||
|
||||
import cats._
|
||||
import cats.implicits._
|
||||
|
||||
case class PointerChain[F[_]](internal: Map[Int, F[Int]])(implicit functor: Functor[F]) {
|
||||
|
||||
def ++(other: PointerChain[F]): PointerChain[F] = {
|
||||
???
|
||||
}
|
||||
|
||||
def map[B](f: ((Int, F[Int])) => B): Vector[B] = internal.map(f).toVector
|
||||
|
||||
}
|
||||
|
||||
object PointerChain {
|
||||
|
||||
def empty[F[_] : Functor]: PointerChain[F] = PointerChain(Map.empty)
|
||||
|
||||
def single[F[_] : Functor](value: F[Nothing]): PointerChain[F] = PointerChain(Map(0 -> value.widen))
|
||||
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
package tf.bug
|
||||
|
||||
import higherkindness.droste.data.Fix
|
||||
|
||||
package object fancadetagless {
|
||||
|
||||
type FancadeW[A] = Fancade
|
||||
type FancadeF = Fix[Fancade]
|
||||
type FancadeW[A] = FancadeF
|
||||
|
||||
}
|
||||
|
|
|
@ -5,10 +5,51 @@ import tf.bug.fancadegraph.BlockDefinition
|
|||
class FancadeSuite extends munit.FunSuite {
|
||||
|
||||
test("A capture creates one block") {
|
||||
val capture = Fancade.Capture[String](BlockDefinition.GetNumber, "hello", Vector(
|
||||
val capture: FancadeF = Fix(Fancade.Capture[String](BlockDefinition.GetNumber, "hello", Vector(
|
||||
BlockDefinition.GetNumber.output
|
||||
)))
|
||||
assertEquals(Fancade.render(capture).blocks.size, 1)
|
||||
}
|
||||
|
||||
test("Stacking 1 + 1 results in a 2-element map") {
|
||||
val one: FancadeF = Fix(Fancade.Capture(BlockDefinition.NumberValue, 1.0f, Vector(
|
||||
BlockDefinition.NumberValue.output
|
||||
)))
|
||||
val add: FancadeF = Fix(Fancade.Use(
|
||||
Vector(one, one),
|
||||
BlockDefinition.AddNumbers,
|
||||
(),
|
||||
Vector(
|
||||
BlockDefinition.AddNumbers.input1,
|
||||
BlockDefinition.AddNumbers.input2
|
||||
),
|
||||
Vector(
|
||||
BlockDefinition.AddNumbers.output
|
||||
)
|
||||
))
|
||||
val stacked = Fancade.stack(add)
|
||||
assertEquals(stacked, Map(
|
||||
1 -> Fancade.Capture(BlockDefinition.NumberValue, 1.0f, Vector(BlockDefinition.NumberValue.output)),
|
||||
0 -> Fancade.Use(Vector(1, 1), BlockDefinition.AddNumbers, (),
|
||||
Vector(
|
||||
BlockDefinition.AddNumbers.input1,
|
||||
BlockDefinition.AddNumbers.input2
|
||||
),
|
||||
Vector(BlockDefinition.AddNumbers.output)
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
test("A constant creates a capture") {
|
||||
val program = new Fanscript.Program[Float] {
|
||||
override def run[F[_]](implicit interp: Fanscript[F]): F[Float] = interp.lift(1.0f)
|
||||
}
|
||||
val result = program.run[FancadeW]
|
||||
assertEquals(result, Fancade.Capture(
|
||||
BlockDefinition.NumberValue,
|
||||
1.0f,
|
||||
Vector(BlockDefinition.NumberValue.output)
|
||||
))
|
||||
assertEquals(capture.render.blocks.size, 1)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue