Skip to content

Commit

Permalink
capture scenario
Browse files Browse the repository at this point in the history
  • Loading branch information
kostmo committed May 15, 2023
1 parent 4384e1b commit 96b303f
Show file tree
Hide file tree
Showing 7 changed files with 454 additions and 0 deletions.
1 change: 1 addition & 0 deletions data/scenarios/Challenges/00-ORDER.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ maypole.yaml
teleport.yaml
2048.yaml
word-search.yaml
capture.yaml
gopher.yaml
ice-cream.yaml
hanoi.yaml
Expand Down
27 changes: 27 additions & 0 deletions data/scenarios/Challenges/_capture/design-commentary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Synchronized capture

Two approaches to implement the goal-checking of this scenario were considered.
In both, the goal is reached when the "pig" is surrounded on all four sides by barriers.

The difference is is when the barriers are not synchronized:
1. In the first approach, a goal condition is defined that detects if any barrier is adjacent to the pig without being surrounded on all sides. The primary goal condition treats this as a negated prerequisite, so that the scenario is instantly lost.
2. In the second (adopted) approach, this secondary goal was preserved, but only as an optional achievement. Instead, the pig as a system robot implements the detection of an un-synchronized approach. The pig retreats a short distance, providing another opportunity for the player to attempt a synchronized approach (though players may simply choose to restart the scenario).

In implementing this second approach, it is essential that the pig react to barrier placement within a single tick. We do not want to allow the player to place one barrier later than the others and simply be too slow to detect this un-synchronized state, counting it as a win. This single-tick reaction is accomplished with the `instant` command.

## Retreat behavior
If a barrier comes in contact with the pig without surrounding on all sides, the pig "runs away" in a straight line opposite from the "base" robot, irrespective of any entities or impassible terrain, until no barriers are adjacent. This may entail traversing multiple cells, which happens instantaneously, due to the single-tick reaction requirement described above.

This escape strategy is simplified by finite availability of `unwalkable`, `portable` entities in this scenario.

## Alternative game mechanic applications

Other physical phenomena could rationalize this "squeezing" game mechanic.

* The synchronized "squeeze" from all sides is evocative of the ["implosion method" of a fission weapon](https://en.wikipedia.org/wiki/Fizzle_(nuclear_explosion)). A [PNE](https://en.wikipedia.org/wiki/Peaceful_nuclear_explosion) has applications in:
* [nuclear synthesis](https://en.wikipedia.org/wiki/Synthesis_of_precious_metals)
* [space travel](https://en.wikipedia.org/wiki/Nuclear_pulse_propulsion)
* [asteroid redirection](https://en.wikipedia.org/wiki/Asteroid_impact_avoidance)
* Instead of an explosion, the barriers could serve as [neutron reflectors](https://en.wikipedia.org/wiki/Neutron_reflector) for a fission reaction. See [demon core](https://en.wikipedia.org/wiki/Demon_core) for consequences of uncontrolled (un-synchronized) reflectors
* A high-pressure squeeze could create [artificial diamonds](https://en.wikipedia.org/wiki/Synthetic_diamond) (HPHT method).

114 changes: 114 additions & 0 deletions data/scenarios/Challenges/_capture/opponent.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
If any blocking entity is touching me, escape.
*/

def elif = \t. \then. \else. {if t then else} end
def else = \t. t end
def abs = \n. if (n < 0) {-n} {n} end;
def doN = \n. \f. if (n > 0) {f; doN (n - 1) f} {}; end;
def until = \p. \c. q <- p; if q {} {c; until p c} end;

def getDirection = \n.
if (n == 0) {
forward;
} $ elif (n == 1) {
right;
} $ elif (n == 2) {
back;
} $ elif (n == 3) {
left;
} $ else {
down;
};
end;

def watchDir = \n.
watch $ getDirection n;
if (n > 0) {
watchDir $ n - 1;
} {};
end;

def boolToInt = \b.
if b {1} {0}
end;

/**
Iterate 4 times (once for each direction)
*/
def countBlocked = \n.
if (n > 0) {
isBlocked <- blocked;
// This assignment has to happen before the recursive
// "countBlocked" call due to #1032
let localBlockCount = boolToInt isBlocked in
turn left;
lastCount <- countBlocked $ n - 1;
return $ lastCount + localBlockCount;
} {
return 0;
}
end;

def reWatch =
watchDir 4;
end;

def locationIsOpen =
emptyHere <- isempty;
blockedCount <- countBlocked 4;
return $ emptyHere && blockedCount == 0;
end;

def faceAwayFrom = \loc.
myLoc <- whereami;
let x = fst loc - fst myLoc in
let y = snd loc - snd myLoc in
let d = if (abs x > abs y) {
if (x > 0) {west} {east}
} {
if (y > 0) {south} {north}
} in
turn d;
end;

/**
Move in opposite direction from base to
find a cross-shaped region that has zero blockages.
*/
def findOpenArea =
baseLoc <- as base {whereami};
faceAwayFrom baseLoc;
until locationIsOpen move;
end;

def relocateAway =
marker <- grab;
findOpenArea;
try {
swap marker;
return ();
} {
place marker;
};
end;

def handleBlockage =
blockedCount <- countBlocked 4;
if (blockedCount > 0) {
if (blockedCount < 4) {
relocateAway;
} {
selfdestruct;
}
} {};
end;

def go =
instant reWatch;
wait 1000;
instant handleBlockage;
go;
end;

go;
123 changes: 123 additions & 0 deletions data/scenarios/Challenges/_capture/solution.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
Need to synchronize pushes so that in the same tick
that the first boulder comes into contact with the
opponent, there is no escape route.
*/

def doN = \n. \f. if (n > 0) {f; doN (n - 1) f} {}; end;

def intersperse = \n. \f2. \f1. if (n > 0) {
f1;
if (n > 1) {
f2;
} {};
intersperse (n - 1) f2 f1;
} {};
end;

def walkAround = \d1. \d2.
turn d1;
doN 3 move;
turn d2;

doN 3 move;
turn d2;
end;

def buildBot = \initialTurn.
_r1 <- build {
watch back;
wait 200;
doN 2 move;
turn initialTurn;
walkAround right left;
push;
};

_r2 <- build {
watch back;
wait 200;
doN 2 move;
turn initialTurn;
walkAround left right;
push;
};
end;

def arrangeBoulders =
// First boulder
turn right;
move;
turn left;
doN 7 move;
turn left;
push;
turn right;
move;
turn left;
move;
turn left;
doN 4 push;

// Second boulder
turn back;
doN 7 move;
turn left;
doN 2 move;
turn left;
doN 5 push;

// Third boulder
turn right;
doN 4 move;
turn left;
doN 7 move;
turn left;
move;
turn left;
doN 3 push;
turn left;
move;
turn right;
move;
turn right;
push;

// Fourth boulder
turn right;
doN 3 move;
turn left;
doN 2 push;
turn right;
move;
turn left;
move;
turn left;
push;
end;

def go =

doN 3 move;
obj <- grab;

arrangeBoulders;

turn right;
doN 2 move;
turn back;
buildBot right;
turn back;
move;

turn left;
move;
buildBot left;
turn back;
move;

place obj;
move;
end;

go;
Loading

0 comments on commit 96b303f

Please sign in to comment.