From ac3bf7d594a515dcd238c8e369960b89720f26c1 Mon Sep 17 00:00:00 2001 From: Nat Lasseter Date: Tue, 16 Jul 2019 11:14:38 +0100 Subject: Initial commit --- .gitignore | 2 + LICENSE | 11 ++++ README.textile | 31 ++++++++++ cube/cube.go | 129 +++++++++++++++++++++++++++++++++++++++ cube/turns.go | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rubiks.go | 61 ++++++++++++++++++ 6 files changed, 424 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.textile create mode 100644 cube/cube.go create mode 100644 cube/turns.go create mode 100644 rubiks.go 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(">: ") + } +} -- cgit v1.2.1