diff options
-rw-r--r-- | pkg/interpreter/interpreter.go | 10 | ||||
-rw-r--r-- | pkg/interpreter/interpreter_test.go | 50 | ||||
-rw-r--r-- | pkg/pointer/pointer.go | 52 | ||||
-rw-r--r-- | pkg/pointer/pointer_test.go | 64 | ||||
-rw-r--r-- | pkg/pointer/stack-stack.go (renamed from pkg/stack/stack-stack.go) | 18 | ||||
-rw-r--r-- | pkg/pointer/stack-stack_test.go (renamed from pkg/stack/stack-stack_test.go) | 39 | ||||
-rw-r--r-- | pkg/pointer/stack.go (renamed from pkg/stack/stack.go) | 2 | ||||
-rw-r--r-- | pkg/pointer/stack_test.go (renamed from pkg/stack/stack_test.go) | 2 |
8 files changed, 206 insertions, 31 deletions
diff --git a/pkg/interpreter/interpreter.go b/pkg/interpreter/interpreter.go index ce2499d..b64e90a 100644 --- a/pkg/interpreter/interpreter.go +++ b/pkg/interpreter/interpreter.go @@ -25,7 +25,8 @@ func (i *Interpreter) Run() { func (i *Interpreter) Step() { var prev *pointer.Pointer = nil for p := i.p; p != nil; p = p.Next { - switch p.Get(*i.f) { + c := p.Get(*i.f) + switch c { case '@': if prev == nil { i.p = p.Next @@ -33,8 +34,13 @@ func (i *Interpreter) Step() { prev.Next = p.Next } break + case '#': + p.Step(*i.f) default: - log.Fatalf("Non implemented instruction code %d : %c", p.Get(*i.f), p.Get(*i.f)) + if !p.Redirect(c) { + log.Fatalf("Non implemented instruction code %d : %c", c, c) + } } + p.Step(*i.f) } } diff --git a/pkg/interpreter/interpreter_test.go b/pkg/interpreter/interpreter_test.go new file mode 100644 index 0000000..ff633a2 --- /dev/null +++ b/pkg/interpreter/interpreter_test.go @@ -0,0 +1,50 @@ +package interpreter + +import ( + "os" + "testing" + + "git.adyxax.org/adyxax/gofunge/pkg/field" + "git.adyxax.org/adyxax/gofunge/pkg/pointer" + "github.com/stretchr/testify/require" +) + +func TestRun(t *testing.T) { + file, err := os.Open("../field/test_data/minimal.b98") + require.NoError(t, err) + defer file.Close() + f, err := field.Load(file) + require.NoError(t, err) + NewInterpreter(f, pointer.NewPointer()).Run() +} + +func TestStep(t *testing.T) { + testCases := []struct { + name string + filename string + pointer *pointer.Pointer + expectedField *field.Field + expectedPointer *pointer.Pointer + }{ + {"minimal", "../field/test_data/minimal.b98", nil, nil, nil}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + file, err := os.Open(tc.filename) + require.NoError(t, err) + defer file.Close() + f, err := field.Load(file) + require.NoError(t, err) + if tc.pointer == nil { + tc.pointer = pointer.NewPointer() + } + NewInterpreter(f, tc.pointer).Step() + if tc.expectedField != nil { + require.Equal(t, tc.expectedField, f) + } + if tc.expectedPointer != nil { + require.Equal(t, tc.expectedPointer, tc.pointer) + } + }) + } +} diff --git a/pkg/pointer/pointer.go b/pkg/pointer/pointer.go index a833f5b..4d847bf 100644 --- a/pkg/pointer/pointer.go +++ b/pkg/pointer/pointer.go @@ -1,6 +1,10 @@ package pointer -import "git.adyxax.org/adyxax/gofunge/pkg/field" +import ( + "math/rand" + + "git.adyxax.org/adyxax/gofunge/pkg/field" +) type Pointer struct { // the position @@ -12,12 +16,14 @@ type Pointer struct { // The Storage offset sox int soy int + // The stack + ss *StackStack // The next element for the multi-"threaded" b98 interpreter Next *Pointer } func NewPointer() *Pointer { - return &Pointer{dx: 1} + return &Pointer{dx: 1, ss: NewStackStack()} } func (p Pointer) Split() *Pointer { @@ -31,3 +37,45 @@ func (p *Pointer) Step(f field.Field) { func (p Pointer) Get(f field.Field) int { return f.Get(p.x, p.y) } + +func (p *Pointer) Set(x, y int) { + p.x, p.y = x, y +} + +func (p *Pointer) RedirectTo(dx, dy int) { + p.dx, p.dy = dx, dy +} + +func (p *Pointer) Reverse() { + p.dx, p.dy = -p.dx, -p.dy +} + +func (p *Pointer) Redirect(c int) bool { + switch c { + case '^': + p.dx, p.dy = 0, -1 + case '>': + p.dx, p.dy = 1, 0 + case 'v': + p.dx, p.dy = 0, 1 + case '<': + p.dx, p.dy = -1, 0 + case '?': + directions := []int{0, -1, 1, 0, 0, 1, -1, 0} + r := 2 * rand.Intn(4) + p.dx, p.dy = directions[r], directions[r+1] + case '[': + p.dx, p.dy = p.dy, -p.dx + case ']': + p.dx, p.dy = -p.dy, p.dx + case 'r': + p.Reverse() + case 'x': + dy := p.ss.Pop() + dx := p.ss.Pop() + p.RedirectTo(dx, dy) + default: + return false + } + return true +} diff --git a/pkg/pointer/pointer_test.go b/pkg/pointer/pointer_test.go index ccbf486..4885d6b 100644 --- a/pkg/pointer/pointer_test.go +++ b/pkg/pointer/pointer_test.go @@ -9,7 +9,7 @@ import ( ) func TestNewPointer(t *testing.T) { - require.Equal(t, NewPointer(), &Pointer{dx: 1}) + require.Equal(t, NewPointer(), &Pointer{dx: 1, ss: NewStackStack()}) } func TestSplit(t *testing.T) { @@ -23,8 +23,8 @@ func TestSplit(t *testing.T) { // We check that p2 is a real copy p.Step(*f) p2.Step(*f) - require.Equal(t, &Pointer{x: 1, y: 0, dx: 1}, p) - require.Equal(t, &Pointer{x: 1, y: 0, dx: 1}, p2) + require.Equal(t, &Pointer{x: 1, y: 0, dx: 1, ss: NewStackStack()}, p) + require.Equal(t, &Pointer{x: 1, y: 0, dx: 1, ss: NewStackStack()}, p2) } func TestStep(t *testing.T) { // Step is thoroughly tested in the field package @@ -50,3 +50,61 @@ func TestGet(t *testing.T) { v := p.Get(*f) require.Equal(t, int('@'), v) } + +func TestSet(t *testing.T) { + p := NewPointer() + p.Set(3, 14) + require.Equal(t, 3, p.x) + require.Equal(t, 14, p.y) +} + +func TestRedirectTo(t *testing.T) { + p := NewPointer() + p.RedirectTo(3, 14) + require.Equal(t, 3, p.dx) + require.Equal(t, 14, p.dy) +} + +func TestReverse(t *testing.T) { + p := NewPointer() + p.RedirectTo(3, 14) + p.Reverse() + require.Equal(t, -3, p.dx) + require.Equal(t, -14, p.dy) +} + +func TestRedirect(t *testing.T) { + testCases := []struct { + name string + input byte + expectedDx int + expectedDy int + }{ + {"up", '^', 0, -1}, + {"right", '>', 1, 0}, + {"down", 'v', 0, 1}, + {"left", '<', -1, 0}, + {"turn left", '[', 14, -3}, + {"turn right", ']', -14, 3}, + {"reverse", 'r', -3, -14}, + {"redirectTo", 'x', 2, 7}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := NewPointer() + p.RedirectTo(3, 14) + p.ss.Push(2) + p.ss.Push(7) + v := p.Redirect(int(tc.input)) + require.Equal(t, true, v) + require.Equal(t, tc.expectedDx, p.dx, "Invalid dx value") + require.Equal(t, tc.expectedDy, p.dy, "Invalid dy value") + }) + } + // We cannot really test random, can we? This just gives coverage + p := NewPointer() + v := p.Redirect(int('?')) + require.Equal(t, true, v) + // A character that does not redirect should return false + require.Equal(t, false, p.Redirect(int('@'))) +} diff --git a/pkg/stack/stack-stack.go b/pkg/pointer/stack-stack.go index 9b80079..6d22f51 100644 --- a/pkg/stack/stack-stack.go +++ b/pkg/pointer/stack-stack.go @@ -1,8 +1,4 @@ -package stack - -import ( - "git.adyxax.org/adyxax/gofunge/pkg/pointer" -) +package pointer type StackStack struct { head *Stack @@ -16,7 +12,7 @@ func NewStackStack() *StackStack { } } -func (ss *StackStack) Begin(p *pointer.Pointer) { +func (ss *StackStack) Begin(p *Pointer) { ss.height++ soss := ss.head n := soss.Pop() @@ -44,7 +40,7 @@ func (ss *StackStack) Begin(p *pointer.Pointer) { p.CalculateNewStorageOffset() } -func (ss *StackStack) End(p *pointer.Pointer) (reflect bool) { +func (ss *StackStack) End(p *Pointer) (reflect bool) { if ss.height == 1 { return true } @@ -89,3 +85,11 @@ func (ss *StackStack) Under() (reflect bool) { } return false } + +func (ss StackStack) Pop() int { + return ss.head.Pop() +} + +func (ss StackStack) Push(v int) { + ss.head.Push(v) +} diff --git a/pkg/stack/stack-stack_test.go b/pkg/pointer/stack-stack_test.go index d2a7949..fd4c9ea 100644 --- a/pkg/stack/stack-stack_test.go +++ b/pkg/pointer/stack-stack_test.go @@ -1,11 +1,10 @@ -package stack +package pointer import ( "os" "testing" "git.adyxax.org/adyxax/gofunge/pkg/field" - "git.adyxax.org/adyxax/gofunge/pkg/pointer" "github.com/stretchr/testify/require" ) @@ -17,7 +16,7 @@ func TestBegin(t *testing.T) { expected.head.next.Push(0) expected.height++ ss := NewStackStack() - p := pointer.NewPointer() + p := NewPointer() ss.Begin(p) require.Equal(t, expected, ss) x, y := p.GetStorageOffset() @@ -40,7 +39,7 @@ func TestBegin(t *testing.T) { expected.head.next.Push(0) expected.head.next.Push(0) expected.height++ - p := pointer.NewPointer() + p := NewPointer() file, err := os.Open("../field/test_data/hello.b98") require.NoError(t, err, "Failed to open file") f, err := field.Load(file) @@ -61,7 +60,7 @@ func TestBegin(t *testing.T) { expected.head.next.Push(2) expected.head.next.Push(3) expected.height++ - p := pointer.NewPointer() + p := NewPointer() p.SetStorageOffset(2, 3) file, err := os.Open("../field/test_data/hello.b98") require.NoError(t, err, "Failed to open file") @@ -87,7 +86,7 @@ func TestBegin(t *testing.T) { expected.head.next.Push(36) expected.head.next.Push(42) expected.height++ - p := pointer.NewPointer() + p := NewPointer() p.SetStorageOffset(36, 42) ss := NewStackStack() ss.head.Push(7) @@ -104,7 +103,7 @@ func TestBegin(t *testing.T) { func TestEnd(t *testing.T) { t.Run("empty", func(t *testing.T) { expected := NewStackStack() - p := pointer.NewPointer() + p := NewPointer() ss := NewStackStack() ss.Begin(p) reflect := ss.End(p) @@ -121,7 +120,7 @@ func TestEnd(t *testing.T) { expected.head.Pop() expected.head.Pop() expected.head.Pop() - p := pointer.NewPointer() + p := NewPointer() ss := NewStackStack() ss.head.Push(7) ss.head.Push(12) @@ -139,7 +138,7 @@ func TestEnd(t *testing.T) { }) t.Run("drop too much", func(t *testing.T) { expected := NewStackStack() - p := pointer.NewPointer() + p := NewPointer() ss := NewStackStack() ss.Begin(p) ss.head.Push(-3) @@ -149,7 +148,7 @@ func TestEnd(t *testing.T) { }) t.Run("reflect", func(t *testing.T) { expected := NewStackStack() - p := pointer.NewPointer() + p := NewPointer() ss := NewStackStack() reflect := ss.End(p) require.Equal(t, true, reflect) @@ -164,7 +163,7 @@ func TestEnd(t *testing.T) { expected.head.Push(14) expected.head.Push(-2) expected.head.Push(5) - p := pointer.NewPointer() + p := NewPointer() ss := NewStackStack() ss.head.size = 4 ss.head.data = make([]int, 4) @@ -195,7 +194,7 @@ func TestUnder(t *testing.T) { require.Equal(t, expected, ss) }) t.Run("positive", func(t *testing.T) { - pe := pointer.NewPointer() + pe := NewPointer() expected := NewStackStack() expected.head.Push(1) expected.head.Push(2) @@ -209,7 +208,7 @@ func TestUnder(t *testing.T) { expected.head.next.Pop() expected.head.next.Pop() expected.head.next.Pop() - p := pointer.NewPointer() + p := NewPointer() ss := NewStackStack() ss.head.Push(1) ss.head.Push(2) @@ -223,7 +222,7 @@ func TestUnder(t *testing.T) { require.Equal(t, expected, ss) }) t.Run("negative", func(t *testing.T) { - pe := pointer.NewPointer() + pe := NewPointer() expected := NewStackStack() expected.Begin(pe) expected.head.next.Push(12) @@ -237,7 +236,7 @@ func TestUnder(t *testing.T) { expected.head.Pop() expected.head.Pop() expected.head.Pop() - p := pointer.NewPointer() + p := NewPointer() ss := NewStackStack() ss.Begin(p) ss.head.Push(8) @@ -249,3 +248,13 @@ func TestUnder(t *testing.T) { require.Equal(t, expected, ss) }) } + +func TestPushPop(t *testing.T) { + ss := NewStackStack() + ss.Push(12) + ss.Push(5) + v := ss.Pop() + require.Equal(t, 5, v) + v = ss.Pop() + require.Equal(t, 12, v) +} diff --git a/pkg/stack/stack.go b/pkg/pointer/stack.go index 4dbb72c..1832dbb 100644 --- a/pkg/stack/stack.go +++ b/pkg/pointer/stack.go @@ -1,4 +1,4 @@ -package stack +package pointer type Stack struct { size int diff --git a/pkg/stack/stack_test.go b/pkg/pointer/stack_test.go index e48c615..1b16085 100644 --- a/pkg/stack/stack_test.go +++ b/pkg/pointer/stack_test.go @@ -1,4 +1,4 @@ -package stack +package pointer import ( "testing" |