mirror of
https://codeberg.org/scip/golsky.git
synced 2025-12-16 20:20:57 +01:00
Added RLE parser by N.Hoffmann and incorporated it onto my gol.
This commit is contained in:
159
rle/pattern_parser.go
Normal file
159
rle/pattern_parser.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package rle
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type TokenType string
|
||||
|
||||
type Token struct {
|
||||
Type TokenType
|
||||
Literal string
|
||||
}
|
||||
|
||||
const (
|
||||
RUN_COUNT = "RUN_COUNT"
|
||||
DEAD_CELL = "DEAD_CELL"
|
||||
ALIVE_CELL = "ALIVE_CELL"
|
||||
EOL = "EOL"
|
||||
EOP = "EOP"
|
||||
)
|
||||
|
||||
type Lexer struct {
|
||||
input string
|
||||
position int
|
||||
readPosition int
|
||||
char byte
|
||||
}
|
||||
|
||||
func NewLexer(input string) *Lexer {
|
||||
l := &Lexer{input: input}
|
||||
l.readChar()
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Lexer) NextToken() Token {
|
||||
var tok Token
|
||||
|
||||
l.skipWhitespace()
|
||||
|
||||
switch l.char {
|
||||
case '$':
|
||||
tok = newToken(EOL, l.char)
|
||||
case '!':
|
||||
tok = newToken(EOP, l.char)
|
||||
case 'b':
|
||||
tok = newToken(DEAD_CELL, l.char)
|
||||
case 'o':
|
||||
tok = newToken(ALIVE_CELL, l.char)
|
||||
default:
|
||||
if isDigit(l.char) {
|
||||
tok.Type = RUN_COUNT
|
||||
tok.Literal = l.readNumber()
|
||||
return tok
|
||||
}
|
||||
}
|
||||
|
||||
l.readChar()
|
||||
return tok
|
||||
}
|
||||
|
||||
func newToken(tokenType TokenType, char byte) Token {
|
||||
return Token{Type: tokenType, Literal: string(char)}
|
||||
}
|
||||
|
||||
type PatternParser struct {
|
||||
lexer *Lexer
|
||||
currentToken Token
|
||||
peekToken Token
|
||||
}
|
||||
|
||||
func NewParser(lexer *Lexer) *PatternParser {
|
||||
p := &PatternParser{
|
||||
lexer: lexer,
|
||||
}
|
||||
p.nextToken()
|
||||
p.nextToken()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (pp *PatternParser) ParsePattern(width, height int) [][]int {
|
||||
result := make([][]int, height)
|
||||
|
||||
row := make([]int, width)
|
||||
var rowIndex int
|
||||
var colIndex int
|
||||
for {
|
||||
switch pp.currentToken.Type {
|
||||
case RUN_COUNT:
|
||||
count, _ := strconv.Atoi(pp.currentToken.Literal)
|
||||
for i := 0; i < count; i++ {
|
||||
switch pp.peekToken.Type {
|
||||
case ALIVE_CELL:
|
||||
row[rowIndex+i] = 1
|
||||
case DEAD_CELL:
|
||||
row[rowIndex+i] = 0
|
||||
case EOL:
|
||||
result[colIndex] = row
|
||||
row = make([]int, width)
|
||||
rowIndex = -1
|
||||
colIndex++
|
||||
}
|
||||
}
|
||||
|
||||
if pp.peekToken.Type != EOL {
|
||||
rowIndex += count - 1
|
||||
}
|
||||
pp.nextToken()
|
||||
case ALIVE_CELL:
|
||||
row[rowIndex] = 1
|
||||
case DEAD_CELL:
|
||||
row[rowIndex] = 0
|
||||
case EOL:
|
||||
result[colIndex] = row
|
||||
row = make([]int, width)
|
||||
rowIndex = -1
|
||||
colIndex++
|
||||
case EOP:
|
||||
result[colIndex] = row
|
||||
return result
|
||||
}
|
||||
rowIndex++
|
||||
pp.nextToken()
|
||||
}
|
||||
}
|
||||
|
||||
func (pp *PatternParser) nextToken() {
|
||||
pp.currentToken = pp.peekToken
|
||||
pp.peekToken = pp.lexer.NextToken()
|
||||
}
|
||||
|
||||
func isDigit(char byte) bool {
|
||||
return '0' <= char && char <= '9'
|
||||
}
|
||||
|
||||
func (l *Lexer) readChar() {
|
||||
if l.readPosition >= len(l.input) {
|
||||
l.char = 0
|
||||
} else {
|
||||
l.char = l.input[l.readPosition]
|
||||
}
|
||||
l.position = l.readPosition
|
||||
l.readPosition++
|
||||
}
|
||||
|
||||
func (l *Lexer) readNumber() string {
|
||||
position := l.position
|
||||
for isDigit(l.char) {
|
||||
l.readChar()
|
||||
}
|
||||
|
||||
return l.input[position:l.position]
|
||||
}
|
||||
|
||||
func (l *Lexer) skipWhitespace() {
|
||||
for l.char == ' ' || l.char == '\t' || l.char == '\n' || l.char == '\r' {
|
||||
l.readChar()
|
||||
}
|
||||
}
|
||||
163
rle/pattern_parser_test.go
Normal file
163
rle/pattern_parser_test.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package rle
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNextToken(t *testing.T) {
|
||||
input := "bo$2bo$3o!"
|
||||
|
||||
tests := []struct {
|
||||
expectedType TokenType
|
||||
expectedLiteral string
|
||||
}{
|
||||
{DEAD_CELL, "b"},
|
||||
{ALIVE_CELL, "o"},
|
||||
{EOL, "$"},
|
||||
{RUN_COUNT, "2"},
|
||||
{DEAD_CELL, "b"},
|
||||
{ALIVE_CELL, "o"},
|
||||
{EOL, "$"},
|
||||
{RUN_COUNT, "3"},
|
||||
{ALIVE_CELL, "o"},
|
||||
{EOP, "!"},
|
||||
}
|
||||
|
||||
l := NewLexer(input)
|
||||
|
||||
for _, test := range tests {
|
||||
token := l.NextToken()
|
||||
|
||||
if token.Type != test.expectedType {
|
||||
t.Errorf("Token typ not correct")
|
||||
}
|
||||
|
||||
if token.Literal != test.expectedLiteral {
|
||||
t.Errorf("Literal not correct")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePattern(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected [][]int
|
||||
width int
|
||||
height int
|
||||
}{
|
||||
{
|
||||
input: "bo$2bo$3o!",
|
||||
expected: [][]int{
|
||||
{0, 1, 0},
|
||||
{0, 0, 1},
|
||||
{1, 1, 1},
|
||||
},
|
||||
width: 3,
|
||||
height: 3,
|
||||
},
|
||||
{
|
||||
input: `24bo$22bobo$12b2o6b2o12b2o$11bo3bo4b2o12b2o$2o8bo5bo3b2o$2o8bo3bob2o4b
|
||||
obo$10bo5bo7bo$11bo3bo$12b2o!`,
|
||||
expected: [][]int{
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
|
||||
{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
width: 36,
|
||||
height: 9,
|
||||
},
|
||||
{
|
||||
input: `20b2o$
|
||||
20b2o4$
|
||||
9b2o$
|
||||
8bo2bo10b2o$
|
||||
9b2o11bo$
|
||||
22bo12bo$
|
||||
23bo10bobo$
|
||||
34bobo$
|
||||
35bo7$
|
||||
32bo2bo$
|
||||
33b3o$
|
||||
2o38b2o$
|
||||
2o38b2o$
|
||||
6b3o$
|
||||
6bo2bo7$
|
||||
6bo$
|
||||
5bobo$
|
||||
5bobo10bo$
|
||||
6bo12bo$
|
||||
19bo11b2o$
|
||||
18b2o10bo2bo$
|
||||
31b2o4$
|
||||
20b2o$
|
||||
20b2o!`,
|
||||
expected: [][]int{
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0},
|
||||
{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
|
||||
{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
|
||||
{0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
width: 42,
|
||||
height: 42,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
l := NewLexer(test.input)
|
||||
pp := NewParser(l)
|
||||
result := pp.ParsePattern(test.width, test.height)
|
||||
|
||||
if !reflect.DeepEqual(result, test.expected) {
|
||||
t.Fatalf(
|
||||
"Patterns do not match.\nExpected: %v\nGot: %v",
|
||||
test.expected,
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
100
rle/rle.go
Normal file
100
rle/rle.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// source: https://github.com/nhoffmann/life by N.Hoffmann 2020.
|
||||
package rle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type RLE struct {
|
||||
Rule string // rule
|
||||
Width int // x
|
||||
Height int // y
|
||||
Pattern [][]int // The actual pattern
|
||||
|
||||
inputLines []string
|
||||
headerLineIndex int
|
||||
patternLineIndex int
|
||||
}
|
||||
|
||||
func Parse(input string) (RLE, error) {
|
||||
rle := RLE{
|
||||
inputLines: strings.Split(input, "\n"),
|
||||
}
|
||||
|
||||
rle.partitionFile()
|
||||
|
||||
err := rle.parseComments()
|
||||
if err != nil {
|
||||
return RLE{}, err
|
||||
}
|
||||
err = rle.parseHeader()
|
||||
if err != nil {
|
||||
return RLE{}, err
|
||||
}
|
||||
err = rle.parsePattern()
|
||||
if err != nil {
|
||||
return RLE{}, err
|
||||
}
|
||||
|
||||
return rle, nil
|
||||
}
|
||||
|
||||
func (rle *RLE) partitionFile() error {
|
||||
for index, line := range rle.inputLines {
|
||||
cleanLine := removeWhitespace(line)
|
||||
if strings.HasPrefix(cleanLine, "x=") {
|
||||
rle.headerLineIndex = index
|
||||
rle.patternLineIndex = index + 1
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("Invlaid input: Header is missing")
|
||||
}
|
||||
|
||||
func (rle *RLE) parseComments() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rle *RLE) parseHeader() (err error) {
|
||||
headerLine := removeWhitespace(rle.inputLines[rle.headerLineIndex])
|
||||
|
||||
headerElements := strings.SplitN(headerLine, ",", 3)
|
||||
|
||||
rle.Width, err = strconv.Atoi(strings.TrimPrefix(headerElements[0], "x="))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rle.Height, err = strconv.Atoi(strings.TrimPrefix(headerElements[1], "y="))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rle.Pattern = make([][]int, rle.Width)
|
||||
|
||||
// check wehter a rule is present, since it's optional
|
||||
if len(headerElements) == 3 {
|
||||
rle.Rule = strings.TrimPrefix(headerElements[2], "rule=")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rle *RLE) parsePattern() error {
|
||||
patternString := strings.Join(rle.inputLines[rle.patternLineIndex:], "")
|
||||
|
||||
l := NewLexer(patternString)
|
||||
pp := NewParser(l)
|
||||
|
||||
rle.Pattern = pp.ParsePattern(rle.Width, rle.Height)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeWhitespace(input string) string {
|
||||
re := regexp.MustCompile(` *\t*\r*\n*`)
|
||||
return re.ReplaceAllString(input, "")
|
||||
}
|
||||
84
rle/rle_test.go
Normal file
84
rle/rle_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package rle
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRLE(t *testing.T) {
|
||||
t.Run("Parse", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expectedPattern [][]int
|
||||
expectedComment string
|
||||
expectedWidth int
|
||||
expectedHeight int
|
||||
expectedRule string
|
||||
}{
|
||||
{
|
||||
input: `#C This is a glider.
|
||||
x = 3, y = 3
|
||||
bo$2bo$3o!`,
|
||||
expectedPattern: [][]int{
|
||||
{0, 1, 0},
|
||||
{0, 0, 1},
|
||||
{1, 1, 1},
|
||||
},
|
||||
expectedWidth: 3,
|
||||
expectedHeight: 3,
|
||||
expectedRule: "",
|
||||
},
|
||||
{
|
||||
input: `#N Gosper glider gun
|
||||
#C This was the first gun discovered.
|
||||
#C As its name suggests, it was discovered by Bill Gosper.
|
||||
x = 36, y = 9, rule = B3/S23
|
||||
24bo$22bobo$12b2o6b2o12b2o$11bo3bo4b2o12b2o$2o8bo5bo3b2o$2o8bo3bob2o4b
|
||||
obo$10bo5bo7bo$11bo3bo$12b2o!`,
|
||||
expectedPattern: [][]int{
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
|
||||
{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
expectedWidth: 36,
|
||||
expectedHeight: 9,
|
||||
expectedRule: "B3/S23",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
rle, err := Parse(test.input)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if rle.Width != test.expectedWidth {
|
||||
t.Errorf("Width dos not match")
|
||||
}
|
||||
|
||||
if rle.Height != test.expectedHeight {
|
||||
t.Errorf("Height does not match")
|
||||
}
|
||||
|
||||
if rle.Rule != test.expectedRule {
|
||||
t.Errorf("Rule does not match")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(rle.Pattern, test.expectedPattern) {
|
||||
t.Errorf(
|
||||
"Patterns do not match.\nExpected: %v\nGot: %v",
|
||||
test.expectedPattern,
|
||||
rle.Pattern,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user