encode.gno
1.82 Kb · 62 lines
1package gno4
2
3import "strings"
4
5// Encode returns the whole game as one string: the 1-based column of each
6// move, in order. An empty game is the empty string, a full game is 42 bytes.
7//
8// The position is not stored, because it is not information: replaying the
9// moves reproduces it exactly, including the winner and the winning line. That
10// is what lets a match be a URL, a chain event and a test fixture without three
11// encoders that can disagree.
12func (b *Board) Encode() string {
13 var sb strings.Builder
14 for _, col := range b.moves {
15 sb.WriteByte(byte('1' + col))
16 }
17 return sb.String()
18}
19
20// Decode replays an encoding produced by [Board.Encode].
21//
22// It replays through [Board.Drop] rather than writing cells directly, so an
23// encoding that describes an impossible game (a column overflowing, a move
24// after the win) is rejected here instead of becoming a board no rule allows.
25// Nothing else in this package can construct an illegal position.
26func Decode(s string) (*Board, error) {
27 b := New()
28 for i := 0; i < len(s); i++ {
29 c := s[i]
30 if c < '1' || c > '7' {
31 return nil, ErrBadEncoding
32 }
33 if _, err := b.Drop(int(c - '1')); err != nil {
34 return nil, ErrBadEncoding
35 }
36 }
37 return b, nil
38}
39
40// String renders the board as seven columns of ASCII, top row first, the way a
41// player looks at it. Empty is '.', Red is 'R', Yellow is 'Y'.
42//
43// This is the debugging view and the one tests assert against. What gnoweb
44// shows is [Board.Image]; what a terminal or a diff shows is this.
45func (b *Board) String() string {
46 var sb strings.Builder
47 for row := Rows - 1; row >= 0; row-- {
48 for col := 0; col < Cols; col++ {
49 switch b.At(col, row) {
50 case Red:
51 sb.WriteByte('R')
52 case Yellow:
53 sb.WriteByte('Y')
54 default:
55 sb.WriteByte('.')
56 }
57 }
58 sb.WriteByte('\n')
59 }
60 sb.WriteString("1234567\n")
61 return sb.String()
62}