forth wizard / stack juggler

This commit is contained in:
zeroflag 2021-08-07 16:03:12 +02:00
parent 390fce921c
commit e1c004bd38
4 changed files with 526 additions and 1 deletions

View file

@ -0,0 +1,325 @@
package com.vectron.fcl;
import com.vectron.fcl.types.Obj;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
public class Juggler {
private final List<Obj> input;
private final List<Obj> output;
private final Stack<Obj> stack;
private final Stack<Obj> rstack;
private final Set<Obj> uniqueOutput;
private final List<Integer> code;
private final List<Word> availableWords;
private final int maxSteps;
private interface Code {
boolean eval(Juggler juggler);
}
private enum Word {
DUP("dup", Juggler::dup),
DROP("drop", Juggler::drop),
OVER("over", Juggler::over),
SWAP("swap", Juggler::swap),
NIP("nip", Juggler::nip),
TUCK("tuck", Juggler::tuck),
ROT("rot", Juggler::rot),
MROT("-rot", Juggler::mrot),
DUP2("2dup", Juggler::dup2),
DROP2("2drop", Juggler::drop2),
SWAP2("2swap", Juggler::swap2),
OVER2("2over", Juggler::over2),
RTO2("2rot", Juggler::rot2),
MRTO2("-2rot", Juggler::mrot2),
RTO(">r", Juggler::rto),
RFROM("r>", Juggler::rfrom);
private final String name;
private final Code code;
Word(String name, Code code) {
this.name = name;
this.code = code;
}
public boolean eval(Juggler juggler) {
return code.eval(juggler);
}
}
public static List<String> solve(List<Obj> input, List<Obj> output, Set<String> excluded, int maxSteps) {
Juggler juggler = new Juggler(input, output, excluded, maxSteps);
return juggler.solve();
}
private Juggler(List<Obj> input, List<Obj> output, Set<String> excluded, int maxSteps) {
this.input = input;
this.output = output;
this.maxSteps = maxSteps;
this.uniqueOutput = new HashSet<>(output);
this.stack = new Stack<>();
this.rstack = new Stack<>();
this.code = new ArrayList<>();
this.availableWords = populateWords(excluded);
}
private List<Word> populateWords(Set<String> excluded) {
List<Word> result = new ArrayList<>();
for (Word each : Word.values()) {
if (!excluded.contains(each.name))
result.add(each);
}
return result;
}
private List<String> solve() {
if (input.isEmpty() && output.isEmpty() || input.equals(output))
return Collections.emptyList();
code.clear();
code.add(0);
while (code.size() <= maxSteps) {
if (goodCode(code))
return result(code);
next(code);
}
return null;
}
private void next(List<Integer> code) {
int i = code.size() -1;
int max = availableWords.size() - 1;
code.set(i, code.get(i) +1);
while (code.get(i) > max) {
code.set(i, 0);
if (i > 0) {
i--;
code.set(i, code.get(i) +1);
} else {
code.add(0, 0);
}
}
}
private boolean goodCode(List<Integer> code) {
stack.clear();
stack.addAll(input);
rstack.clear();
List<Stack<Obj>> stackHistory = new ArrayList<>();
List<Stack<Obj>> rstackHistory = new ArrayList<>();
for (int i = 0; i < code.size(); i++) {
if (!nthWord(code.get(i)).eval(this) || nop() || cycle(stackHistory, rstackHistory)) {
skip(i, code);
return false;
}
stackHistory.add(copy(stack));
rstackHistory.add(copy(rstack));
}
return rstack.isEmpty() && stack.equals(output);
}
private Word nthWord(int n) {
return availableWords.get(n);
}
private void skip(int n, List<Integer> code) {
int max = availableWords.size() -1;
for (int i = n +1; i < code.size(); i++) {
code.set(i, max);
}
}
private boolean cycle(List<Stack<Obj>> stackHistory, List<Stack<Obj>> rstackHistory) {
for (int i = 0; i < stackHistory.size(); i++) {
if (stackHistory.get(i).equals(stack) && rstackHistory.get(i).equals(rstack))
return true;
}
return false;
}
private boolean nop() {
return rstack.isEmpty() && stack.equals(input);
}
private Stack<Obj> copy(Stack<Obj> stack) {
Stack<Obj> result = new Stack<>();
result.addAll(stack);
return result;
}
private List<String> result(List<Integer> code) {
List<String> result = new ArrayList<>();
for (Integer each : code)
result.add(nthWord(each).name);
return result;
}
private Obj pick(int i) {
return stack.get(stack.size() - i);
}
private boolean dup() {
if (stack.empty()) return false;
stack.push(pick(1));
return true;
}
private boolean dup2() {
if (stack.size() < 2) return false;
stack.push(pick(2));
stack.push(pick(2));
return true;
}
private boolean drop2() {
if (stack.size() < 2) return false;
stack.pop();
stack.pop();
return !missing();
}
private boolean drop() {
if (stack.empty()) return false;
stack.pop();
return !missing();
}
private boolean swap() {
if (stack.size() < 2) return false;
Obj n1 = stack.pop();
Obj n2 = stack.pop();
stack.push(n1);
stack.push(n2);
return true;
}
private boolean swap2() {
if (stack.size() < 4) return false;
Obj n1 = stack.pop();
Obj n2 = stack.pop();
Obj n3 = stack.pop();
Obj n4 = stack.pop();
stack.push(n2);
stack.push(n1);
stack.push(n4);
stack.push(n3);
return true;
}
private boolean over() {
if (stack.size() < 2) return false;
stack.push(pick(2));
return true;
}
private boolean over2() {
if (stack.size() < 4) return false;
stack.push(pick(4));
stack.push(pick(4));
return true;
}
private boolean nip() {
if (stack.size() < 2) return false;
Obj n = stack.pop();
stack.pop();
stack.push(n);
return !missing();
}
private boolean tuck() {
if (stack.size() < 2) return false;
Obj n1 = stack.pop();
Obj n2 = stack.pop();
stack.push(n1);
stack.push(n2);
stack.push(n1);
return true;
}
private boolean rot() {
if (stack.size() < 3) return false;
Obj n1 = stack.pop();
Obj n2 = stack.pop();
Obj n3 = stack.pop();
stack.push(n2);
stack.push(n1);
stack.push(n3);
return true;
}
private boolean mrot() {
if (stack.size() < 3) return false;
Obj n1 = stack.pop();
Obj n2 = stack.pop();
Obj n3 = stack.pop();
stack.push(n1);
stack.push(n3);
stack.push(n2);
return true;
}
private boolean rot2() {
if (stack.size() < 6) return false;
Obj n1 = stack.pop();
Obj n2 = stack.pop();
Obj n3 = stack.pop();
Obj n4 = stack.pop();
Obj n5 = stack.pop();
Obj n6 = stack.pop();
stack.push(n4);
stack.push(n3);
stack.push(n2);
stack.push(n1);
stack.push(n6);
stack.push(n5);
return true;
}
private boolean mrot2() {
if (stack.size() < 6) return false;
Obj n1 = stack.pop();
Obj n2 = stack.pop();
Obj n3 = stack.pop();
Obj n4 = stack.pop();
Obj n5 = stack.pop();
Obj n6 = stack.pop();
stack.push(n2);
stack.push(n1);
stack.push(n6);
stack.push(n5);
stack.push(n4);
stack.push(n3);
return true;
}
private boolean rfrom() {
if (rstack.size() < 1) return false;
stack.push(rstack.pop());
return true;
}
private boolean rto() {
if (stack.size() < 1) return false;
rstack.push(stack.pop());
return true;
}
private boolean missing() {
for (Obj each : uniqueOutput) {
if (!stack.contains(each) && !stack.contains(each))
return true;
}
return false;
}
}

View file

@ -7,7 +7,7 @@
: 2swap rot >r rot r> ; : 2swap rot >r rot r> ;
: 2rot swap >r >r 2swap r> r> swap 2swap ; : 2rot swap >r >r 2swap r> r> swap 2swap ;
: -2rot 2rot 2rot ; : -2rot 2rot 2rot ;
: 2over 2swap 2dup 2rot 2swap ; : 2over 2swap 2dup -2rot ;
: tuck swap over ; : tuck swap over ;
: != = not ; : != = not ;
: >= < not ; : >= < not ;

View file

@ -130,6 +130,7 @@ public class FclTest {
public void testJuggling() { // http://sovietov.com/app/forthwiz.html public void testJuggling() { // http://sovietov.com/app/forthwiz.html
assertEquals(asList(2l, 1l), evalGetStack("1 2 swap")); assertEquals(asList(2l, 1l), evalGetStack("1 2 swap"));
assertEquals(asList(1l, 2l, 1l), evalGetStack("1 2 over")); assertEquals(asList(1l, 2l, 1l), evalGetStack("1 2 over"));
assertEquals(asList(1l, 2l, 3l, 4l, 1l, 2l), evalGetStack("1 2 3 4 2over"));
assertEquals(asList(3l, 3l), evalGetStack("3 dup")); assertEquals(asList(3l, 3l), evalGetStack("3 dup"));
assertEquals(asList(3l, 4l, 3l, 4l), evalGetStack("3 4 2dup")); assertEquals(asList(3l, 4l, 3l, 4l), evalGetStack("3 4 2dup"));
assertEquals(asList(6l), evalGetStack("5 6 nip")); assertEquals(asList(6l), evalGetStack("5 6 nip"));

View file

@ -0,0 +1,199 @@
package com.vectron.fcl;
import com.vectron.fcl.types.Num;
import com.vectron.fcl.types.Obj;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.vectron.fcl.types.Num.ONE;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class JugglerTest {
private final Set<String> excluded = new HashSet<>();
@Test
public void testEmpty() {
assertTrue(solve(emptyList(), emptyList()).isEmpty());
}
@Test
public void testSame() {
assertTrue(solve(asList(ONE), asList(ONE)).isEmpty());
}
@Test
public void testSingleDrop() {
assertSolution("drop", "1", "");
}
@Test
public void testSingleDup() {
assertSolution("dup", "0", "0 0");
}
@Test
public void testSingleSwap() {
assertSolution("swap", "0 1", "1 0");
}
@Test
public void testSingle2Swap() {
assertSolution("2swap", "0 1 2 3", "2 3 0 1");
}
@Test
public void testSingleOver() {
assertSolution("over", "0 1", "0 1 0");
}
@Test
public void testSingle2Over() {
assertSolution("2over", "0 1 2 3", "0 1 2 3 0 1");
}
@Test
public void testSingleNip() {
assertSolution("nip", "0 1", "1");
}
@Test
public void testSingleTuck() {
assertSolution("tuck", "0 1", "1 0 1");
}
@Test
public void testSingleRot() {
assertSolution("rot", "0 1 2", "1 2 0");
}
@Test
public void testSingle2Rot() {
assertSolution("2rot", "0 1 2 3 4 5", "2 3 4 5 0 1");
}
@Test
public void testSingle2mRot() {
assertSolution("-2rot", "0 1 2 3 4 5", "4 5 0 1 2 3");
}
@Test
public void testSingleMRot() {
assertSolution("-rot", "0 1 2", "2 0 1");
}
@Test
public void testSingle2dup() {
assertSolution("2dup", "0 1", "0 1 0 1");
}
@Test
public void testReverse3() {
assertSolution("swap rot", "0 1 2", "2 1 0");
}
@Test
public void testReverse4() {
assertSolution("swap 2swap swap", "0 1 2 3", "3 2 1 0");
excluded.add("2swap");
assertSolution("over tuck -2rot 2drop swap", "0 1 2 3", "3 2 1 0");
excluded.add("-2rot");
assertSolution("swap 2over swap 2rot 2drop", "0 1 2 3", "3 2 1 0");
excluded.add("2over");
assertSolution("rot >r -rot r> rot", "0 1 2 3", "3 2 1 0");
}
@Test
public void testReverse5() {
assertSolution("over 2swap 2rot -rot nip", "0 1 2 3 4", "4 3 2 1 0");
}
@Test
public void testOverOver() {
excluded.add("2dup");
assertSolution("over over", "0 1", "0 1 0 1");
}
@Test
public void testSingle2drop() {
assertSolution("2drop", "0 1", "");
}
@Test
public void testComplex1() {
assertSolution("drop over swap 2swap", "0 1 2 3", "1 2 0 1");
excluded.add("2swap");
assertSolution("drop over >r rot r>", "0 1 2 3", "1 2 0 1");
}
@Test
public void testComplex2() {
assertSolution("drop tuck 2swap", "0 1 2 3", "1 2 0 2");
excluded.add("2swap");
assertSolution("drop rot over", "0 1 2 3", "1 2 0 2");
}
@Test
public void testComplex3() {
assertSolution("drop rot dup", "0 1 2 3", "1 2 0 0");
}
@Test
public void testComplex4() {
assertSolution("swap 2over drop 2swap nip", "0 1 2 3", "0 2 0 3");
excluded.add("2over");
assertSolution("rot drop >r over r>", "0 1 2 3", "0 2 0 3");
}
@Test
public void testComplex5() {
assertSolution("drop rot drop -rot", "0 1 2 3 4", "3 0 2");
}
@Test
public void testComplex6() {
assertSolution("drop 2drop nip over swap", "0 1 2 3 4 5", "0 0 2");
}
@Test
public void testComplex7() {
assertSolution("drop 2drop nip 2dup rot", "0 1 2 3 4 5", "0 0 2 2");
}
@Test
public void testComplex8() {
assertSolution("drop 2drop swap -rot dup", "0 1 2 3 4 5", "1 0 2 2");
}
@Test
public void testNoSolution1() {
assertSolution(null, "0 1 2 3 4 5", "0 0 2 1");
}
private List<String> solve(List<Obj> input, List<Obj> output) {
return Juggler.solve(input, output, excluded, 5);
}
private void assertSolution(String expected, String input, String output) {
if (expected == null)
assertEquals(null, solve(parse(input), parse(output)));
else
assertEquals(Arrays.asList(expected.split(" ")), solve(parse(input), parse(output)));
}
private List<Obj> parse(String str) {
List<Obj> result = new ArrayList<>();
if (str.equals("")) return result;
for (String each : str.split(" "))
result.add(Num.parse(each));
return result;
}
}