aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNat Lasseter <nat.lasseter@exa.net.uk>2019-07-16 11:14:38 +0100
committerNat Lasseter <nat.lasseter@exa.net.uk>2019-07-16 11:14:38 +0100
commitac3bf7d594a515dcd238c8e369960b89720f26c1 (patch)
tree3cb575c81b8bb91ab148e1f3e0ec66acff7f5c63
Initial commit
-rw-r--r--.gitignore2
-rw-r--r--LICENSE11
-rw-r--r--README.textile31
-rw-r--r--cube/cube.go129
-rw-r--r--cube/turns.go190
-rw-r--r--rubiks.go61
6 files changed, 424 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9443fcd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+rubiks
+*.swp
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a91386d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,11 @@
+BSD 2-Clause License
+
+Copyright (c) 2019, Nat Lasseter All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.textile b/README.textile
new file mode 100644
index 0000000..28ca3e3
--- /dev/null
+++ b/README.textile
@@ -0,0 +1,31 @@
+h1. Rubiks
+
+A Rubiks cube library (rubiks/cube) and TUI (rubiks) written in Go.
+
+h2. Why?
+
+Because. Bored.
+
+h2. Coordinates
+
+Okay, this bit is weird because I was hacking it as I went. (0,0,0) is the right front top cubelet.
+
+- X-axis := Right to left
+- Y-axis := Front to back
+- Z-axis := Top to bottom
+
+h2. Display
+
+@func (cube) String() string@ displays the cube in the form:
+
+pre.. UUU
+ UUU
+ UUU
+LLL FFF RRR BBB
+LLL FFF RRR BBB
+LLL FFF RRR BBB
+ DDD
+ DDD
+ DDD
+
+p. Note that this is the net of the cube, and so the rear face is mirrored right-to-left, as it has been unfolded around from the back.
diff --git a/cube/cube.go b/cube/cube.go
new file mode 100644
index 0000000..f52cf8f
--- /dev/null
+++ b/cube/cube.go
@@ -0,0 +1,129 @@
+package cube
+
+import (
+ "errors"
+ "strings"
+)
+
+type colour uint8
+
+const (
+ White colour = iota
+ Yellow
+ Red
+ Orange
+ Blue
+ Green
+)
+
+func (c colour) String() string {
+ switch c {
+ case White:
+ return "W"
+ case Yellow:
+ return "Y"
+ case Red:
+ return "R"
+ case Orange:
+ return "O"
+ case Blue:
+ return "B"
+ case Green:
+ return "G"
+ }
+ return ""
+}
+
+type row []colour
+
+func (r row) String() (s string) {
+ for i := range r {
+ s += r[i].String()
+ }
+ return
+}
+
+type face []row
+
+func (f face) colGet(c int) (r row) {
+ r = make(row, len(f))
+ for i := 0; i < len(f); i++ {
+ r[i] = f[i][c]
+ }
+ return
+}
+
+func (f *face) colSet(c int, r row) {
+ for i := 0; i < len(*f); i++ {
+ (*f)[i][c] = r[i]
+ }
+}
+
+type Cube struct {
+ front face
+ back face
+ top face
+ bottom face
+ left face
+ right face
+}
+
+func New(size int) (*Cube, error) {
+ if size < 1 {
+ return nil, errors.New("Size must be greater than 0.")
+ }
+ var c Cube
+ c.front = make(face, size)
+ c.back = make(face, size)
+ c.top = make(face, size)
+ c.bottom = make(face, size)
+ c.left = make(face, size)
+ c.right = make(face, size)
+
+ for i := 0; i < size; i++ {
+ c.front[i] = make(row, size)
+ c.back[i] = make(row, size)
+ c.top[i] = make(row, size)
+ c.bottom[i] = make(row, size)
+ c.left[i] = make(row, size)
+ c.right[i] = make(row, size)
+
+ for j := 0; j < size; j++ {
+ c.front[i][j] = Red
+ c.back[i][j] = Orange
+ c.top[i][j] = White
+ c.bottom[i][j] = Yellow
+ c.left[i][j] = Green
+ c.right[i][j] = Blue
+ }
+ }
+
+ return &c, nil
+}
+
+func (c Cube) Size() int {
+ return len(c.front)
+}
+
+func (c Cube) String() (r string) {
+ s := strings.Repeat(" ", c.Size()+2)
+
+ for i := 0; i < c.Size(); i++ {
+ r += s + c.top[i].String() + "\n"
+ }
+ r += "\n"
+
+ for i := 0; i < c.Size(); i++ {
+ r += c.left[i].String() + " " +
+ c.front[i].String() + " " +
+ c.right[i].String() + " " +
+ c.back[i].String() + "\n"
+ }
+ r += "\n"
+
+ for i := 0; i < c.Size(); i++ {
+ r += s + c.bottom[i].String() + "\n"
+ }
+
+ return
+}
diff --git a/cube/turns.go b/cube/turns.go
new file mode 100644
index 0000000..2129f26
--- /dev/null
+++ b/cube/turns.go
@@ -0,0 +1,190 @@
+package cube
+
+func (a *row) flip() {
+ for left, right := 0, len(*a)-1; left < right; left, right = left+1, right-1 {
+ (*a)[left], (*a)[right] = (*a)[right], (*a)[left]
+ }
+}
+
+func (f *face) transpose() {
+ l := len(*f)
+ nf := make(face, l)
+
+ for i := 0; i < l; i++ {
+ nf[i] = make(row, l)
+ for j := 0; j < l; j++ {
+ nf[i][j] = (*f)[j][i]
+ }
+ }
+
+ for i := 0; i < l; i++ {
+ for j := 0; j < l; j++ {
+ (*f)[i][j] = nf[i][j]
+ }
+ }
+}
+
+func (f *face) clockwise() {
+ f.transpose()
+
+ for i := 0; i < len(*f); i++ {
+ (*f)[i].flip()
+ }
+}
+
+func (f *face) _clockwise() {
+ for i := 0; i < len(*f); i++ {
+ (*f)[i].flip()
+ }
+
+ f.transpose()
+}
+
+func (c *Cube) Y(layer int) {
+ _layer := c.Size() - layer - 1
+
+ if layer == 0 {
+ c.top.clockwise()
+ } else if _layer == 0 {
+ c.bottom._clockwise()
+ }
+
+ temp := c.front[layer]
+ c.front[layer] = c.right[layer]
+ c.right[layer] = c.back[layer]
+ c.back[layer] = c.left[layer]
+ c.left[layer] = temp
+}
+
+func (c *Cube) Y_(layer int) {
+ _layer := c.Size() - layer - 1
+
+ if layer == 0 {
+ c.top._clockwise()
+ } else if _layer == 0 {
+ c.bottom.clockwise()
+ }
+
+ temp := c.front[layer]
+ c.front[layer] = c.left[layer]
+ c.left[layer] = c.back[layer]
+ c.back[layer] = c.right[layer]
+ c.right[layer] = temp
+}
+
+func (c *Cube) U() {
+ c.Y(0)
+}
+func (c *Cube) U_() {
+ c.Y_(0)
+}
+
+func (c *Cube) D() {
+ c.Y_(c.Size() - 1)
+}
+func (c *Cube) D_() {
+ c.Y(c.Size() - 1)
+}
+
+func (c *Cube) Z(layer int) {
+ _layer := c.Size() - layer - 1
+
+ if layer == 0 {
+ c.front.clockwise()
+ } else if _layer == 0 {
+ c.back._clockwise()
+ }
+
+ temp := c.top[_layer]
+ c.top[_layer] = c.left.colGet(_layer)
+ c.top[_layer].flip()
+ c.left.colSet(_layer, c.bottom[layer])
+ c.bottom[layer] = c.right.colGet(layer)
+ c.bottom[layer].flip()
+ c.right.colSet(layer, temp)
+}
+
+func (c *Cube) Z_(layer int) {
+ _layer := c.Size() - layer - 1
+
+ if layer == 0 {
+ c.front._clockwise()
+ } else if _layer == 0 {
+ c.back.clockwise()
+ }
+
+ temp := c.top[_layer]
+ c.top[_layer] = c.right.colGet(layer)
+ c.bottom[layer].flip()
+ c.right.colSet(layer, c.bottom[layer])
+ c.bottom[layer] = c.left.colGet(layer)
+ temp.flip()
+ c.left.colSet(_layer, temp)
+}
+
+func (c *Cube) F() {
+ c.Z(0)
+}
+func (c *Cube) F_() {
+ c.Z_(0)
+}
+
+func (c *Cube) B() {
+ c.Z_(c.Size() - 1)
+}
+func (c *Cube) B_() {
+ c.Z(c.Size() - 1)
+}
+
+func (c *Cube) X(layer int) {
+ _layer := c.Size() - layer - 1
+
+ if layer == 0 {
+ c.right.clockwise()
+ } else if _layer == 0 {
+ c.left._clockwise()
+ }
+
+ temp := c.top.colGet(_layer)
+ temp.flip()
+ c.top.colSet(_layer, c.front.colGet(_layer))
+ c.front.colSet(_layer, c.bottom.colGet(_layer))
+ t := c.back.colGet(layer)
+ t.flip()
+ c.bottom.colSet(_layer, t)
+ c.back.colSet(layer, temp)
+}
+
+func (c *Cube) X_(layer int) {
+ _layer := c.Size() - layer - 1
+
+ if layer == 0 {
+ c.right._clockwise()
+ } else if _layer == 0 {
+ c.left.clockwise()
+ }
+
+ temp := c.top.colGet(_layer)
+ t := c.back.colGet(layer)
+ t.flip()
+ c.top.colSet(_layer, t)
+ t = c.bottom.colGet(_layer)
+ t.flip()
+ c.back.colSet(layer, t)
+ c.bottom.colSet(_layer, c.front.colGet(_layer))
+ c.front.colSet(_layer, temp)
+}
+
+func (c *Cube) R() {
+ c.X(0)
+}
+func (c *Cube) R_() {
+ c.X_(0)
+}
+
+func (c *Cube) L() {
+ c.X_(c.Size() - 1)
+}
+func (c *Cube) L_() {
+ c.X(c.Size() - 1)
+}
diff --git a/rubiks.go b/rubiks.go
new file mode 100644
index 0000000..3ecf605
--- /dev/null
+++ b/rubiks.go
@@ -0,0 +1,61 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "rubiks/cube"
+ "strconv"
+ "strings"
+)
+
+func handle(c *cube.Cube, cmd string) {
+ cmd = strings.ToLower(strings.TrimSpace(cmd))
+ switch cmd {
+ case "q":
+ os.Exit(0)
+ case "u":
+ c.U()
+ case "u'":
+ c.U_()
+ case "d":
+ c.D()
+ case "d'":
+ c.D_()
+ case "r":
+ c.R()
+ case "r'":
+ c.R_()
+ case "l":
+ c.L()
+ case "l'":
+ c.L_()
+ case "f":
+ c.F()
+ case "f'":
+ c.F_()
+ case "b":
+ c.B()
+ case "b'":
+ c.B_()
+ }
+}
+
+func main() {
+ s := 3
+ if len(os.Args) > 1 {
+ s, _ = strconv.Atoi(os.Args[1])
+ }
+
+ c, _ := cube.New(s)
+ scanner := bufio.NewScanner(os.Stdin)
+
+ fmt.Print(c)
+ fmt.Print(">: ")
+ for scanner.Scan() {
+ handle(c, scanner.Text())
+ fmt.Println()
+ fmt.Print(c)
+ fmt.Print(">: ")
+ }
+}