### Update Tetris tutorials

master
Trevor Slocum 2 years ago
parent
commit
09858990a0
2 changed files with 111 additions and 10 deletions
1. 17
content/post/tetris-1.md
2. 104
content/post/tetris-2.md

#### 17 content/post/tetris-1.md Unescape Escape View File

 `@ -62,6 +62,7 @@ so we will store minos as slices of points.` @@ -62,6 +62,7 @@ so we will store minos as slices of points. `*Example coordinate positions in 10x10 playfield*` ``` ``` `{{< highlight go >}}` `// Point is an X,Y coordinate.` `type Point struct {` ` X, Y int` `}` `@ -71,6 +72,7 @@ func (p Point) Rotate180() Point { return Point{-p.X, -p.Y} }` @@ -71,6 +72,7 @@ func (p Point) Rotate180() Point { return Point{-p.X, -p.Y} } `func (p Point) Rotate270() Point { return Point{-p.Y, p.X} }` `func (p Point) Reflect() Point { return Point{-p.X, p.Y} }` ``` ``` `// Mino is a set of Points.` `type Mino []Point` ``` ``` `var exampleMino = Mino{{0, 0}, {1, 0}, {2, 0}, {1, 1}} // T piece` `@ -116,6 +118,7 @@ func (m Mino) String() string {` @@ -116,6 +118,7 @@ func (m Mino) String() string { ` return b.String()` `}` ``` ``` `// Render returns a visual representation of a Mino.` `func (m Mino) Render() string {` ` var (` ` w, h = m.Size()` `@ -163,6 +166,7 @@ translated to `(0, 0), (1, 0), (2, 0), (1, 1)`:` @@ -163,6 +166,7 @@ translated to `(0, 0), (1, 0), (2, 0), (1, 1)`: `*Translating a mino to `(0,0)`*` ``` ``` `{{< highlight go >}}` `// minCoords returns the lowest coordinate of a Mino.` `func (m Mino) minCoords() (int, int) {` ` minx := m[0].X` ` miny := m[0].Y` `@ -179,6 +183,7 @@ func (m Mino) minCoords() (int, int) {` @@ -179,6 +183,7 @@ func (m Mino) minCoords() (int, int) { ` return minx, miny` `}` ``` ``` `// Origin returns a translated Mino located at 0,0 and with positive coordinates only.` `func (m Mino) Origin() Mino {` ` minx, miny := m.minCoords()` ``` ``` `@ -205,6 +210,7 @@ XXX X` @@ -205,6 +210,7 @@ XXX X `Flatten calculates the flattest side of a mino and returns a flattened mino.` ``` ``` `{{< highlight go >}}` `// Size returns the dimensions of a Mino.` `func (m Mino) Size() (int, int) {` ` var x, y int` ` for _, p := range m {` `@ -219,6 +225,7 @@ func (m Mino) Size() (int, int) {` @@ -219,6 +225,7 @@ func (m Mino) Size() (int, int) { ` return x + 1, y + 1` `}` ``` ``` `// Flatten calculates the flattest side of a Mino and returns a flattened Mino.` `func (m Mino) Flatten() Mino {` ` var (` ` w, h = m.Size()` `@ -281,6 +288,7 @@ XXX -> XX XXX XX` @@ -281,6 +288,7 @@ XXX -> XX XXX XX `*Variations of a mino*` ``` ``` `{{< highlight go >}}` `// Variations returns the three other rotations of a Mino.` `func (m Mino) Variations() []Mino {` ` v := make([]Mino, 3)` ` for i := 0; i < 3; i++ {` `@ -300,6 +308,7 @@ func (m Mino) Variations() []Mino {` @@ -300,6 +308,7 @@ func (m Mino) Variations() []Mino { `Canonical returns a flattened mino translated to `0,0`.` ``` ``` `{{< highlight go >}}` `// Canonical returns a flattened Mino translated to 0,0.` `func (m Mino) Canonical() Mino {` ` var (` ` ms = m.Origin().String()` `@ -339,6 +348,7 @@ X -> XX -> XXX XX -> XXXX XX XXX XXX XXX XX XX` @@ -339,6 +348,7 @@ X -> XX -> XXX XX -> XXXX XX XXX XXX XXX XX XX `Neighborhood returns the [Von Neumann neighborhood](https://en.wikipedia.org/wiki/Von_Neumann_neighborhood) of a point.` ``` ``` `{{< highlight go >}} ` `//Neighborhood returns the Von Neumann neighborhood of a Point.` `func (p Point) Neighborhood() []Point {` ` return []Point{` ` {p.X - 1, p.Y},` `@ -351,6 +361,7 @@ func (p Point) Neighborhood() []Point {` @@ -351,6 +361,7 @@ func (p Point) Neighborhood() []Point { `NewPoints calculates the neighborhood of each point of a mino and returns only the new points.` ``` ``` `{{< highlight go >}}` `// Neighborhood returns the Von Neumann neighborhood of a Point.` `func (m Mino) HasPoint(p Point) bool {` ` for _, mp := range m {` ` if mp == p {` `@ -361,6 +372,7 @@ func (m Mino) HasPoint(p Point) bool {` @@ -361,6 +372,7 @@ func (m Mino) HasPoint(p Point) bool { ` return false` `}` ``` ``` `// NewPoints calculates the neighborhood of each Point of a Mino and returns only the new Points.` `func (m Mino) NewPoints() []Point {` ` var newPoints []Point` ``` ``` `@ -381,6 +393,7 @@ func (m Mino) NewPoints() []Point {` @@ -381,6 +393,7 @@ func (m Mino) NewPoints() []Point { `NewMinos returns a new mino for every new neighborhood point of a supplied mino.` ``` ``` `{{< highlight go >}}` `// NewMinos returns a new Mino for every new neighborhood Point of a supplied Mino.` `func (m Mino) NewMinos() []Mino {` ` points := m.NewPoints()` ``` ``` `@ -401,10 +414,12 @@ We generate minos for the rank below the requested rank and iterate over the` @@ -401,10 +414,12 @@ We generate minos for the rank below the requested rank and iterate over the `variations of each mino, saving and returning all unique variations.` ``` ``` `{{< highlight go >}}` `// monomino returns a single-block Mino.` `func monomino() Mino {` ` return Mino{{0, 0}}` `}` ``` ``` `// Generate procedurally generates Minos of a supplied rank.` `func Generate(rank int) ([]Mino, error) {` ` switch {` ` case rank < 0:` `@ -443,4 +458,4 @@ func Generate(rank int) ([]Mino, error) {` @@ -443,4 +458,4 @@ func Generate(rank int) ([]Mino, error) { ``` ``` `# Up next: The Matrix` ``` ``` `In [part two](/post/tetris-2/) we will create a [matrix](https://tetris.wiki/Playfield) to hold our minos and implement [SRS rotation](https://tetris.wiki/SRS).` `In [part two](/post/tetris-2/) we create a [Matrix](https://tetris.wiki/Playfield) to hold our Minos and implement [SRS rotation](https://tetris.wiki/SRS).`

#### 104 content/post/tetris-2.md Unescape Escape View File

 `@ -65,6 +65,7 @@ A block is an integer representing the contents of a single X-Y Point (see part` @@ -65,6 +65,7 @@ A block is an integer representing the contents of a single X-Y Point (see part `We will use four of these blocks to build each tetromino.` ``` ``` `{{< highlight go >}}` `// Block represents the content of a single location within a Matrix.` `type Block int` ``` ``` `const (` `@ -86,6 +87,7 @@ The matrix has a width, height and buffer height.` @@ -86,6 +87,7 @@ The matrix has a width, height and buffer height. `The buffer is additional space above the visible playfield.` ``` ``` `{{< highlight go >}}` `// Matrix is a 2D grid of Blocks.` `type Matrix struct {` ` W int // Width` ` H int // Height` `@ -94,12 +96,13 @@ type Matrix struct {` @@ -94,12 +96,13 @@ type Matrix struct { ` M []Block // Contents` `}` ``` ``` `func NewMatrix(w int, h int, b int) *Matrix {` `// NewMatrix returns a new Matrix.` `func NewMatrix(width int, height int, buffer int) *Matrix {` ` m := Matrix{` ` W: w,` ` H: h,` ` B: b,` ` M: make([]Block, w*(h+b)),` ` W: width,` ` H: height,` ` B: buffer,` ` M: make([]Block, width*(height+buffer)),` ` }` ``` ``` ` return &m` `@ -109,14 +112,16 @@ func NewMatrix(w int, h int, b int) *Matrix {` @@ -109,14 +112,16 @@ func NewMatrix(w int, h int, b int) *Matrix { `To retrieve the contents of a point, we calculate its index by multiplying the Y coordinate with the matrix width and adding the X coordinate.` ``` ``` `{{< highlight go >}}` `func I(x int, y int, w int) int {` ` if x < 0 || x >= w || y < 0 {` `// I returns the slice index position of the specified coordinates.` `func I(x int, y int, width int) int {` ` if x < 0 || x >= width || y < 0 {` ` log.Panicf("failed to retrieve index for %d,%d width %d: invalid coordinates", x, y, w)` ` }` ` ` ` return (y * w) + x` ``` ``` ` return (y * width) + x` `}` ``` ``` `// Block returns the block at the specified coordinates, or BlockNone.` `func (m *Matrix) Block(x int, y int) Block {` ` if y >= m.H+m.B {` ` log.Panicf("failed to retrieve block at %d,%d: invalid y coordinate", x, y)` `@ -126,3 +131,84 @@ func (m *Matrix) Block(x int, y int) Block {` @@ -126,3 +131,84 @@ func (m *Matrix) Block(x int, y int) Block { ` return m.M[index]` `}` `{{< / highlight >}}` ``` ``` `To display the Matrix, a `Render` function must be implemented as we did in part 1.` ``` ``` `{{< highlight go >}}` `// Render returns a visual representation of a Matrix.` `func (m *Matrix) Render() string {` ` var b strings.Builder` ``` ``` ` for y := m.H - 1; y >= 0; y-- {` ` b.WriteRune('<')` ` b.WriteRune('!')` ``` ``` ` for x := 0; x < m.W; x++ {` ` block := m.Block(x, y)` ` if block != BlockNone {` ` b.WriteRune('X')` ` } else {` ` b.WriteRune(' ')` ` }` ` }` ``` ``` ` b.WriteRune('!')` ` b.WriteRune('>')` ` b.WriteRune('\n')` ` }` ``` ``` ` // Bottom border` ``` ``` ` b.WriteRune('<')` ` b.WriteRune('!')` ``` ``` ` for x := 0; x < m.W; x++ {` ` b.WriteRune('=')` ` }` ``` ``` ` b.WriteRune('!')` ` b.WriteRune('>')` ` b.WriteRune('\n')` ``` ``` ` b.WriteString(` \/\/\/\/\/`)` ` b.WriteRune('\n')` ``` ``` ` return b.String()` `}` `{{< / highlight >}}` ``` ``` `Let's create a new Matrix and add some blocks to it.` `The resulting output should match the Matrix at the top of this tutorial.` ``` ``` `{{< highlight go >}}` `func main() {` ` matrix := NewMatrix(10, 20, 20)` ``` ``` ` matrix.M[I(2, 0, matrix.W)] = BlockSolidBlue` ``` ``` ` matrix.M[I(5, 2, matrix.W)] = BlockSolidCyan` ``` ``` ` matrix.M[I(4, 5, matrix.W)] = BlockSolidRed` ``` ``` ` matrix.M[I(1, 7, matrix.W)] = BlockSolidYellow` ``` ``` ` matrix.M[I(3, 15, matrix.W)] = BlockSolidMagenta` ``` ``` ` fmt.Print(matrix.Render())` `}` `{{< / highlight >}}` ``` ``` `We are currently rendering the matrix as text without any color information.` `To properly support colors, and to allow more advanced interface elements, we` `will instead use a terminal-based user interface library, [cview](https://gitlab.com/tslocum/cview).` `This will be covered in part three.` ` ` `## Rotation` ``` ``` `*Coming soon*` ``` ``` `See [Piece](https://gitlab.com/tslocum/netris/-/blob/master/pkg/mino/piece.go).` ``` ``` `# Up next: The User Interface` ``` ``` `In part three we will implement a terminal-based user interface with support for colors using [cview](https://gitlab.com/tslocum/cview).`