From 62aaf967e7e70095a4417b35bf2e4589ccd5497e Mon Sep 17 00:00:00 2001
From: Maas Lalani <maas@lalani.dev>
Date: Tue, 21 Dec 2021 23:24:53 -0500
Subject: [PATCH] extract position

---
 board/display.go                     | 92 ++++++++++++++++++++--------
 board/position.go                    | 25 --------
 position/position.go                 | 40 ++++++++++++
 {board => position}/position_test.go |  2 +-
 4 files changed, 109 insertions(+), 50 deletions(-)
 delete mode 100644 board/position.go
 create mode 100644 position/position.go
 rename {board => position}/position_test.go (97%)

diff --git a/board/display.go b/board/display.go
index f2f385f..e44dd1e 100644
--- a/board/display.go
+++ b/board/display.go
@@ -3,14 +3,19 @@ package board
 import (
 	"fmt"
 	"strings"
+
+	"github.com/maaslalani/gambit/position"
 )
 
 var (
-	border = []string{
-		"┌", "┬", "┐",
-		"├", "┼", "┤",
-		"└", "┴", "┘",
-	}
+	border = []string{"┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}
+
+	borderBottomOffset = 6
+	borderMiddleOffset = 3
+	borderTopOffset    = 0
+
+	files = []string{"A", "B", "C", "D", "E", "F", "G", "H"}
+	ranks = []int{7, 6, 5, 4, 3, 2, 1, 0}
 )
 
 const (
@@ -19,35 +24,39 @@ const (
 	marginLeft = "    "
 )
 
-var (
-	files = []string{"A", "B", "C", "D", "E", "F", "G", "H"}
-	ranks = []int{7, 6, 5, 4, 3, 2, 1, 0}
-)
-
+// String prints the board in a human readable format.
+// The left and bottom sides have labels for ranks and files respectively
+// All pieces are surrounded with borders.
 func (b Board) String() string {
 	var s string
+
 	if b.reversed {
-		ranks = []int{0, 1, 2, 3, 4, 5, 6, 7}
+		ranks = reverse(ranks)
 	}
+
 	for r, row := range ranks {
-		if r == 0 {
-			s += buildRow(border[0], border[1], border[2])
-			s += "\n"
+
+		if isFirstRow(r) {
+			s += buildRow(borderTopOffset) + "\n"
 		}
+
 		for c, cell := range b.grid[row] {
-			if c == 0 {
-				s += fmt.Sprintf(" %d  ", row+1)
+			if isFirstColumn(c) {
+				s += fmt.Sprintf(" %d  ", position.RowToRank(row))
 			}
+
 			s += fmt.Sprintf("%s %s ", vertical, cell)
-			if c == len(ranks)-1 {
+
+			if isLastColumn(c) {
 				s += vertical
 			}
 		}
-		if r < len(b.grid)-1 {
-			s += buildRow(border[3], border[4], border[5])
+
+		if !isLastRow(r) {
+			s += buildRow(borderMiddleOffset)
 		} else {
-			s += buildRow(border[6], border[7], border[8])
-			s += "\n  " + marginLeft
+			s += buildRow(borderBottomOffset)
+			s += "\n      "
 			s += strings.Join(files, "   ")
 		}
 		s += "\n"
@@ -56,12 +65,47 @@ func (b Board) String() string {
 	return s
 }
 
-func buildRow(left, middle, right string) string {
+// isLastRow returns whether or not the given row is the last row of the
+// board based on the number of ranks on the board.
+func isLastRow(i int) bool {
+	return i == len(ranks)-1
+}
+
+// isLastColumn returns whether or not the given column is the last column of
+// the board based on the number of files on the board.
+func isLastColumn(i int) bool {
+	return i == len(files)-1
+}
+
+// isFirstRow returns whether or not the given row is the first row
+func isFirstRow(i int) bool {
+	return i == 0
+}
+
+// isFirstColumn returns whether or not the given column is the first column
+func isFirstColumn(i int) bool {
+	return i == 0
+}
+
+// buildRow builds a row string based on the given borders for the left and
+// right side and correctly pads the middle with the given character adjusted
+// to the number of rows on the board.
+func buildRow(borderOffset int) string {
+	var left, middle, right = border[borderOffset], border[borderOffset+1], border[borderOffset+2]
 	var row []string
 	row = append(row, left)
-	for i := 0; i < 7; i++ {
+	for i := 0; i < len(ranks)-1; i++ {
 		row = append(row, middle)
 	}
 	row = append(row, right)
-	return fmt.Sprintf("\n%s%s", marginLeft, strings.Join(row, horizontal+horizontal+horizontal))
+	return fmt.Sprintf("\n    %s", strings.Join(row, horizontal+horizontal+horizontal))
+}
+
+// reverse reverses the given slice of ints.
+func reverse(ranks []int) []int {
+	var reversed []int
+	for i := len(ranks) - 1; i >= 0; i-- {
+		reversed = append(reversed, ranks[i])
+	}
+	return reversed
 }
diff --git a/board/position.go b/board/position.go
deleted file mode 100644
index 4a50be2..0000000
--- a/board/position.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package board
-
-import (
-	"fmt"
-	"strconv"
-)
-
-type Position struct {
-	Row int // rank
-	Col int // file
-}
-
-func (p Position) String() string {
-	// Given a position in row, column form we return a readable
-	// string that represents a rank and file such as E4 or G7
-	return fmt.Sprintf("%c%d", p.Col+'A', p.Row+1)
-}
-
-func ToPosition(s string) Position {
-	// Given a string such as E4 or G7, we return a position in
-	// row, column form to be used as a position in the board's
-	parsed, _ := strconv.Atoi(string(s[1]))
-	row := parsed - 1
-	return Position{row, int(s[0] - 'A')}
-}
diff --git a/position/position.go b/position/position.go
new file mode 100644
index 0000000..e43dbc2
--- /dev/null
+++ b/position/position.go
@@ -0,0 +1,40 @@
+package position
+
+import (
+	"fmt"
+	"strconv"
+)
+
+type Position struct {
+	Row int // rank
+	Col int // file
+}
+
+func (p Position) String() string {
+	// Given a position in row, column form we return a readable
+	// string that represents a rank and file such as E4 or G7
+	return fmt.Sprintf("%c%d", ColumnToFile(p.Col), RowToRank(p.Row))
+}
+
+func ToPosition(s string) Position {
+	// Given a string such as E4 or G7, we return a position in
+	// row, column form to be used as a position in the board's
+	return Position{RankToRow(s[1]), FileToColumn(s[0])}
+}
+
+func RowToRank(row int) int {
+	return row + 1
+}
+
+func RankToRow(rank byte) int {
+	parsed, _ := strconv.Atoi(string(rank))
+	return parsed - 1
+}
+
+func ColumnToFile(column int) string {
+	return fmt.Sprintf("%c", column+'A')
+}
+
+func FileToColumn(file byte) int {
+	return int(file - 'A')
+}
diff --git a/board/position_test.go b/position/position_test.go
similarity index 97%
rename from board/position_test.go
rename to position/position_test.go
index a6b9e20..797e875 100644
--- a/board/position_test.go
+++ b/position/position_test.go
@@ -1,4 +1,4 @@
-package board
+package position
 
 import "testing"