From 6d70cc9953d73cc74191a8c9c5c8db1647338ac1 Mon Sep 17 00:00:00 2001
From: Daniel Perelman <perelman@cs.washington.edu>
Date: Wed, 13 Sep 2023 00:31:08 -0700
Subject: [PATCH] Interpreter: added some support for turn alone and slice.

---
 www/js/interpreter.ts | 73 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)

diff --git a/www/js/interpreter.ts b/www/js/interpreter.ts
index 1752e4d..4123d99 100644
--- a/www/js/interpreter.ts
+++ b/www/js/interpreter.ts
@@ -64,6 +64,20 @@ function handsInLine(args: { wavy: boolean } & ({ which: ShortLinesPosition, fac
     ]);
   }
 }
+function handToDancerToSideInCircleFacingAcross(which: CirclePosition): Map<Hand, HandConnection> {
+  return new Map<Hand, HandConnection>([
+    which.isOnLeftLookingAcross()
+      ? [Hand.Right, { hand: Hand.Left, to: HandTo.DancerRight }]
+      : [Hand.Left, { hand: Hand.Right, to: HandTo.DancerLeft }]
+  ]);
+}
+function handToDancerToSideInCircleFacingUpOrDown(which: CirclePosition): Map<Hand, HandConnection> {
+  return new Map<Hand, HandConnection>([
+    which.isOnLeftLookingUpAndDown()
+      ? [Hand.Right, { hand: Hand.Left, to: HandTo.DancerRight }]
+      : [Hand.Left, { hand: Hand.Right, to: HandTo.DancerLeft }]
+  ]);
+}
 
 function balanceCircleInAndOut(move: Move, startPos: SemanticPosition, balanceBeats?: number): [LowLevelMove, LowLevelMove] {
   if (startPos.kind !== PositionKind.Circle) {
@@ -1240,6 +1254,45 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
         }], startingPos);
       });
 
+    case "slice":
+      if (move.parameters["slice increment"] === "dancer") {
+        // TODO Maybe this only actually gets used to move an entire couple by going diagonal back?
+        throw new Error("Slicing by a single dancer is unsupported.");
+      }
+
+      const sliceLeft = move.parameters.slide;
+      const sliceReturns = move.parameters["slice return"] !== "none";
+
+      const sliceForwardBeats = sliceReturns ? move.beats / 2 : move.beats;
+      const sliceBackwardBeats = move.beats - sliceForwardBeats;
+
+      return handleCircleMove(({ startPos }) => {
+        const startingPos: SemanticPosition & { setOffset: number } = {
+          kind: PositionKind.Circle,
+          which: startPos.which,
+          facing: startPos.which.facingAcross(),
+          hands: handToDancerToSideInCircleFacingAcross(startPos.which),
+          setOffset: startPos.setOffset ?? 0,
+          lineOffset: startPos.lineOffset,
+        };
+        const sliceAmount = startingPos.which.isLeft() === sliceLeft ? +0.5 : -0.5;
+        const forwardOffset = startingPos.setOffset + sliceAmount;
+        const endOffset = move.parameters["slice return"] === "diagonal" ? forwardOffset + sliceAmount : forwardOffset;
+
+        const sliceForward: PartialLowLevelMove = {
+          beats: sliceForwardBeats,
+          endPosition: { ...startingPos, setOffset: forwardOffset, longLines: LongLines.Forward },
+          movementPattern: { kind: SemanticAnimationKind.Linear },
+        };
+        const maybeSliceBackward: PartialLowLevelMove[] = sliceReturns ? [{
+          beats: sliceBackwardBeats,
+          endPosition: { ...startingPos, setOffset: endOffset },
+          movementPattern: { kind: SemanticAnimationKind.Linear },
+        }] : [];
+
+        return combine([sliceForward, ...maybeSliceBackward], startingPos);
+      });
+
     case "down the hall":
       if (move.parameters.who !== "everyone") {
         throw new Error("Don't know what it means for not everyone to go down the hall.");
@@ -1683,6 +1736,26 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
         return combine(heySteps.map(heyStepToPartialLowLevelMove), { ...startingPos, hands: undefined });
       });
 
+    case "turn alone":
+      if (move.parameters.who !== "everyone" || move.beats !== 0) {
+        throw new Error("turn alone unsupported except for changing to new circle.");
+      }
+      return handleCircleMove(({startPos}) => {
+        const which = startPos.which.swapUpAndDown();
+        const startAndEndPos: SemanticPosition = {
+          ...startPos,
+          which,
+          facing: which.facingUpOrDown(),
+          setOffset: (startPos.setOffset ?? 0) + (startPos.which.isTop() ? +0.5 : -0.5),
+        }
+
+        return combine([{
+          beats: move.beats,
+          endPosition: startAndEndPos,
+          movementPattern: { kind: SemanticAnimationKind.StandStill },
+        }], startAndEndPos);
+      })
+
     case "custom":
       if (move.parameters.custom.includes("mirrored mad robin")) {
         return handleCircleMove(({ id, startPos }) => {