Skip to content

Commit

Permalink
initial booster design
Browse files Browse the repository at this point in the history
  • Loading branch information
Dynom committed Jul 10, 2018
1 parent 6d794a4 commit d754780
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
75 changes: 75 additions & 0 deletions keyboard/booster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package keyboard

import (
"math"
)

var (
keyDistances map[string]Coordinates
keyboardLayouts = map[string][]string{
"qwerty-us": {
"`1234567890-=",
" qwertyuiop[]\\",
" asdfghjkl;'",
" zxcvbnm,./",
},
}
)

type Coordinates struct {
X float64
Y float64
}

func init() {
keyDistances = generateKeyDistance(keyboardLayouts["qwerty-us"])
}

func GetBestMatch(input string, list []string) (string, float64) {
var scores = make([]float64, len(list))
for listOffset, ref := range list {
for i := 0; i < len(input); i++ {
if i >= len(ref) {
// @todo missing characters, we should add a penalty
scores[listOffset] += 1
continue
}

left, right := string(input[i]), string(ref[i])
scores[listOffset] += getDistance(keyDistances[left], keyDistances[right])
}
}

var bestScore = math.Inf(1)
var offset int
for listOffset, score := range scores {
if score < bestScore {
bestScore = score
offset = listOffset
}
}

return list[offset], scores[offset]
}

func getDistance(a, b Coordinates) float64 {
return math.Sqrt(
math.Pow(b.X-a.X, 2) + math.Pow(b.Y-a.Y, 2),
)
}

func generateKeyDistance(rows []string) map[string]Coordinates {
var keyMap = make(map[string]Coordinates, len(rows))

for rowIndex, row := range rows {
for column := 0; column < len(row); column++ {
character := string(row[column])
keyMap[character] = Coordinates{
X: float64(column),
Y: float64(rowIndex),
}
}
}

return keyMap
}
73 changes: 73 additions & 0 deletions keyboard/booster_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package keyboard

import (
"math"
"testing"
)

const (
floatTolerance = 0.01
)

func TestGetBestMatch(t *testing.T) {
testData := []struct {
Input string
List []string
Expect string
}{
{Input: "bee4", List: []string{"beer", "beef"}, Expect: "beer"},
{Input: "bee5", List: []string{"beef", "beer"}, Expect: "beer"},
{Input: "bee5", List: []string{"beef", "beer", "beast"}, Expect: "beer"},
{Input: "bee5", List: []string{"beef", "beer", "ben"}, Expect: "beer"},
}

for _, td := range testData {
result, distance := GetBestMatch(td.Input, td.List)
if td.Expect != result {
t.Errorf("Expected '%s' to match '%s', instead I got '%s' with distance %f, %+v",
td.Input, td.Expect, result, distance, td)
}
}
}

func TestGetDistance(t *testing.T) {
testData := []struct {
A Coordinates
B Coordinates
Distance float64
}{
{A: Coordinates{X: 0, Y: 0}, B: Coordinates{X: 0, Y: 100}, Distance: 100},
{A: Coordinates{X: 0, Y: 0}, B: Coordinates{X: 100, Y: 0}, Distance: 100},
{A: Coordinates{X: 1, Y: 2}, B: Coordinates{X: 1, Y: 2}, Distance: 0},
{A: Coordinates{X: 10, Y: 20}, B: Coordinates{X: 20, Y: 10}, Distance: 14.14},
}

for _, td := range testData {
d := getDistance(td.A, td.B)

if math.Abs(d-td.Distance) > floatTolerance {
t.Errorf("Expected the distance to be %f, instead I got %f\n%v", td.Distance, d, td)
}
}

}

func TestGenerateKeyDistance(t *testing.T) {
table := generateKeyDistance([]string{
"abc", // 00, 01, 02
"def", // 10, 11, 12
"ghi", // 20, 21, 22
})

if table["a"].X != 0 || table["a"].Y != 0 {
t.Errorf("Expected the coords to be at 0,0 %+v", table["a"])
}

if table["e"].X != 1 || table["e"].Y != 1 {
t.Errorf("Expected the coords to be at 1,1 %+v", table["i"])
}

if table["i"].X != 2 || table["i"].Y != 2 {
t.Errorf("Expected the coords to be at 2,2 %+v", table["i"])
}
}

0 comments on commit d754780

Please sign in to comment.