Update Tetris part 1
This commit is contained in:
parent
470d1df32f
commit
481a343f09
|
@ -27,13 +27,13 @@ googleAnalytics = "" # Enable Google Analytics by entering your tracking id
|
|||
mathjax = false # Enable MathJax
|
||||
mathjaxPath = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js" # Specify MathJax path
|
||||
mathjaxConfig = "TeX-AMS-MML_HTMLorMML" # Specify MathJax config
|
||||
highlightColor = "#e22d30" # Override highlight color
|
||||
highlightColor = "#0b5693" # Override highlight color
|
||||
customCSS = ["css/custom.css"] # Include custom CSS files
|
||||
customJS = ["js/custom.js"] # Include custom JS files
|
||||
|
||||
[Params.sidebar]
|
||||
home = "right" # Configure layout for home page
|
||||
list = "left" # Configure layout for list pages
|
||||
list = "right" # Configure layout for list pages
|
||||
single = false # Configure layout for single pages
|
||||
# Enable widgets in given order
|
||||
widgets = ["social", "recent"]
|
||||
|
|
|
@ -1,47 +1,41 @@
|
|||
---
|
||||
title: "Textris - Creating a terminal-based Tetris clone - Part 1: Pieces and playfield"
|
||||
#date: 2019-11-26T01:42:18-07:00
|
||||
title: "Terminal-based Tetris - Part 1: Procedural polyomino generation"
|
||||
date: 2020-01-18T08:30:00-07:00
|
||||
categories: [tutorial]
|
||||
draft: true
|
||||
aliases:
|
||||
- /post/textris-1/
|
||||
---
|
||||
|
||||
This is the first in a series of tutorials on creating a [terminal-based](https://en.wikipedia.org/wiki/Text-based_user_interface) [Tetris](https://en.wikipedia.org/wiki/Tetris) clone with [Go](https://golang.org).
|
||||
This is the first part of a series of tutorials on creating a [terminal-based](https://en.wikipedia.org/wiki/Text-based_user_interface)
|
||||
[Tetris](https://en.wikipedia.org/wiki/Tetris) clone with [Go](https://golang.org).
|
||||
|
||||
Game pieces (minos) are generated and added to a playfield (matrix).
|
||||
For a complete implementation of a Tetris clone in Go, see [netris](https://git.sr.ht/~tslocum/netris).
|
||||
|
||||
## Disclaimer
|
||||
# Disclaimer
|
||||
|
||||
Tetris is a registered trademark of the Tetris Holding, LLC.
|
||||
|
||||
Rocket Nine Labs is in no way affiliated with Tetris Holding, LLC.
|
||||
|
||||
## Contents
|
||||
|
||||
* [Minos](#minos)
|
||||
* [Data model](#data-model)
|
||||
* [Generation](#generation)
|
||||
* [Comparing and sorting](#comparing-and-sorting)
|
||||
* [Generating new minos](#generating-new-minos)
|
||||
* [Matrix](#matrix)
|
||||
* [Data model](#data-model)
|
||||
|
||||
# Minos
|
||||
## Minos
|
||||
|
||||
Game pieces are called "minos" because they are [polyominos](https://en.wikipedia.org/wiki/Polyomino).
|
||||
This tutorial focuses on the seven one-sided [terominos](https://en.wikipedia.org/wiki/Tetromino), where each piece has four blocks.
|
||||
This tutorial series will focus on the seven one-sided [terominos](https://en.wikipedia.org/wiki/Tetromino),
|
||||
where each piece has four blocks.
|
||||
|
||||
```
|
||||
XX X X X XX XX
|
||||
XXXX XX XXX XXX XXX XX XX
|
||||
|
||||
I O T J L S Z
|
||||
```
|
||||
|
||||
The number of blocks a mino has is also known as its rank.
|
||||
|
||||
| I | O | T | J | L | S | Z |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| <pre><br>████</pre> | <pre>██<br>██</pre> | <pre> █<br>███</pre> | <pre>█<br>███</pre> | <pre> █<br>███</pre> | <pre> ██<br>██</pre> | <pre>██<br> ██</pre> |
|
||||
### Mino data model
|
||||
|
||||
## Data model
|
||||
|
||||
Tetris is played on an [X-Y grid](https://en.wikipedia.org/wiki/Cartesian_coordinate_system).
|
||||
We will store Minos as slices of points.
|
||||
Tetris is played on an [X-Y grid](https://en.wikipedia.org/wiki/Cartesian_coordinate_system),
|
||||
so we will store minos as slices of points.
|
||||
|
||||
{{< highlight go >}}
|
||||
type Point struct {
|
||||
|
@ -55,19 +49,20 @@ func (p Point) Reflect() Point { return Point{-p.X, p.Y} }
|
|||
|
||||
type Mino []Point
|
||||
|
||||
var minoT = Mino{{0, 0}, {1, 0}, {2, 0}, {1, 1}}
|
||||
var exampleMino = Mino{{0, 0}, {1, 0}, {2, 0}, {1, 1}} // T piece
|
||||
{{< / highlight >}}
|
||||
|
||||
## Generation
|
||||
### Generating minos
|
||||
|
||||
Instead of hard-coding each piece into our game, let's procedurally generate them.
|
||||
This allows us to play with any size of mino.
|
||||
|
||||
### Comparing and sorting
|
||||
### Sorting and comparing minos
|
||||
|
||||
To compare minos efficiently while generating, we will compare their string representation.
|
||||
We will define a String method which sorts the coordinates before printing.
|
||||
This allows us to compare duplicate minos just by checking their string values.
|
||||
To compare minos efficiently while generating, we will define a String method
|
||||
which sorts a mino's coordinates before printing them.
|
||||
|
||||
This will allow us to identify duplicate minos by comparing their string values.
|
||||
|
||||
{{< highlight go >}}
|
||||
func (m Mino) Len() int { return len(m) }
|
||||
|
@ -98,7 +93,11 @@ func (m Mino) String() string {
|
|||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
Origin returns a translated mino located at 0,0 and with positive coordinates only.
|
||||
Origin returns a translated mino located at `0,0` and with positive coordinates
|
||||
only.
|
||||
|
||||
A mino with the coordinates `(-3, -1), (-2, -1), (-1, -1), (-2, 0)` would be
|
||||
translated to `(0, 0), (1, 0), (2, 0), (1, 1)`.
|
||||
|
||||
{{< highlight go >}}
|
||||
func (m Mino) minCoords() (int, int) {
|
||||
|
@ -130,9 +129,10 @@ func (m Mino) Origin() Mino {
|
|||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
Another transformation is applied not only to help identify duplicate minos, but also to retrieve their initial rotation, as [pieces should spawn flat-side down](https://tetris.wiki/Super_Rotation_System).
|
||||
Another transformation is applied not only to help identify duplicate minos,
|
||||
but also to retrieve their initial rotation, as [pieces should spawn flat-side down](https://tetris.wiki/Super_Rotation_System).
|
||||
|
||||
The flattest side is calculated and a flattened mino is returned.
|
||||
Flatten calculates the flattest side of a mino and returns a flattened mino.
|
||||
|
||||
{{< highlight go >}}
|
||||
func (m Mino) Flatten() Mino {
|
||||
|
@ -178,9 +178,8 @@ func (m Mino) Flatten() Mino {
|
|||
}
|
||||
|
||||
newMino := make(Mino, len(m))
|
||||
copy(newMino, m)
|
||||
for i := 0; i < len(m); i++ {
|
||||
newMino[i] = rotateFunc(newMino[i])
|
||||
newMino[i] = rotateFunc(m[i])
|
||||
}
|
||||
|
||||
return newMino
|
||||
|
@ -206,7 +205,7 @@ func (m Mino) Variations() []Mino {
|
|||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
Canonical returns a flattened mino translated to 0,0.
|
||||
Canonical returns a flattened mino translated to `0,0`.
|
||||
|
||||
{{< highlight go >}}
|
||||
func (m Mino) Canonical() Mino {
|
||||
|
@ -233,7 +232,10 @@ func (m Mino) Canonical() Mino {
|
|||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
### Generating new minos
|
||||
### Generating additional minos
|
||||
|
||||
Starting with a monomino (a mino with a single point: `0,0`), we will generate
|
||||
additional minos by adding neighboring points.
|
||||
|
||||
Neighborhood returns the [Von Neumann neighborhood](https://en.wikipedia.org/wiki/Von_Neumann_neighborhood) of a point.
|
||||
|
||||
|
@ -292,8 +294,13 @@ func (m Mino) NewMinos() []Mino {
|
|||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
### Generating unique minos
|
||||
|
||||
Generate procedurally generates minos of a supplied rank.
|
||||
|
||||
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 >}}
|
||||
func Generate(rank int) ([]Mino, error) {
|
||||
switch {
|
||||
|
@ -335,73 +342,6 @@ func monomino() Mino {
|
|||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
# Matrix
|
||||
# Stay tuned...
|
||||
|
||||
The matrix is typically 10 blocks wide and 20 blocks high.
|
||||
|
||||
## Data model
|
||||
|
||||
A block is an integer representing the contents of a single X-Y coordinate on the matrix.
|
||||
|
||||
{{< highlight go >}}
|
||||
type Block int
|
||||
|
||||
const (
|
||||
BlockNone Block = iota
|
||||
BlockSolidBlue
|
||||
BlockSolidCyan
|
||||
BlockSolidRed
|
||||
BlockSolidYellow
|
||||
BlockSolidMagenta
|
||||
BlockSolidGreen
|
||||
BlockSolidOrange
|
||||
)
|
||||
{{< / highlight >}}
|
||||
|
||||
The matrix will be stored as a slice of blocks.
|
||||
The zero-value of Block is a blank space.
|
||||
|
||||
The matrix has a width, height and buffer height.
|
||||
The buffer is additional space above the visible playfield.
|
||||
|
||||
{{< highlight go >}}
|
||||
type Matrix struct {
|
||||
W int // Width
|
||||
H in // Height
|
||||
B int // Buffer height
|
||||
|
||||
M []Block // Contents
|
||||
}
|
||||
|
||||
func NewMatrix(w int, h int, b int) *Matrix {
|
||||
m := Matrix{
|
||||
W: w,
|
||||
H: h,
|
||||
B: b,
|
||||
M: make([]Block, w*(h+b)),
|
||||
}
|
||||
|
||||
return &m
|
||||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
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 {
|
||||
log.Panicf("failed to retrieve index for %d,%d width %d: invalid coordinates", x, y, w)
|
||||
}
|
||||
|
||||
return (y * w) + x
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
index := I(x, y, m.W)
|
||||
return m.M[index]
|
||||
}
|
||||
{{< / highlight >}}
|
||||
In part two we will create a [matrix](https://tetris.wiki/Playfield) to hold our minos and implement [SRS rotation](https://tetris.wiki/SRS).
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
title: "Terminal-based Tetris - Part 2: The matrix"
|
||||
#date: 2019-11-26T01:42:18-07:00
|
||||
categories: [tutorial]
|
||||
draft: true
|
||||
aliases:
|
||||
- /post/textris-2/
|
||||
---
|
||||
|
||||
This is the second part of a series of tutorials on creating a [terminal-based](https://en.wikipedia.org/wiki/Text-based_user_interface) [Tetris](https://en.wikipedia.org/wiki/Tetris) clone with [Go](https://golang.org).
|
||||
|
||||
For a complete implementation of a Tetris clone in Go, see [netris](https://git.sr.ht/~tslocum/netris).
|
||||
|
||||
# Disclaimer
|
||||
|
||||
Tetris is a registered trademark of the Tetris Holding, LLC.
|
||||
|
||||
Rocket Nine Labs is in no way affiliated with Tetris Holding, LLC.
|
||||
|
||||
## Matrix
|
||||
|
||||
The matrix is typically 10 blocks wide and 20 blocks high.
|
||||
|
||||
### Matrix data model
|
||||
|
||||
A block is an integer representing the contents of a single X-Y coordinate on the matrix.
|
||||
|
||||
{{< highlight go >}}
|
||||
type Block int
|
||||
|
||||
const (
|
||||
BlockNone Block = iota
|
||||
BlockSolidBlue
|
||||
BlockSolidCyan
|
||||
BlockSolidRed
|
||||
BlockSolidYellow
|
||||
BlockSolidMagenta
|
||||
BlockSolidGreen
|
||||
BlockSolidOrange
|
||||
)
|
||||
{{< / highlight >}}
|
||||
|
||||
The matrix will be stored as a slice of blocks.
|
||||
The zero-value of Block is a blank space.
|
||||
|
||||
The matrix has a width, height and buffer height.
|
||||
The buffer is additional space above the visible playfield.
|
||||
|
||||
{{< highlight go >}}
|
||||
type Matrix struct {
|
||||
W int // Width
|
||||
H in // Height
|
||||
B int // Buffer height
|
||||
|
||||
M []Block // Contents
|
||||
}
|
||||
|
||||
func NewMatrix(w int, h int, b int) *Matrix {
|
||||
m := Matrix{
|
||||
W: w,
|
||||
H: h,
|
||||
B: b,
|
||||
M: make([]Block, w*(h+b)),
|
||||
}
|
||||
|
||||
return &m
|
||||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
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 {
|
||||
log.Panicf("failed to retrieve index for %d,%d width %d: invalid coordinates", x, y, w)
|
||||
}
|
||||
|
||||
return (y * w) + x
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
index := I(x, y, m.W)
|
||||
return m.M[index]
|
||||
}
|
||||
{{< / highlight >}}
|
Loading…
Reference in New Issue