Skip to content

Commit b825f2b

Browse files
feat: add 7-bag generation
1 parent 3aaff1b commit b825f2b

File tree

4 files changed

+173
-116
lines changed

4 files changed

+173
-116
lines changed

bag.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"math/rand"
6+
)
7+
8+
var RotationCoords = map[byte][]Coordinate{
9+
'I': {
10+
{X: -1, Y: -1},
11+
{X: 2, Y: 1},
12+
{X: -2, Y: -2},
13+
{X: 1, Y: 2},
14+
},
15+
'O': {
16+
{X: 0, Y: 0},
17+
{X: 0, Y: 0},
18+
{X: 0, Y: 0},
19+
{X: 0, Y: 0},
20+
},
21+
'6': { // All tetriminos with 6 cells (T, S, Z, J, L)
22+
{X: 0, Y: 0},
23+
{X: 1, Y: 0},
24+
{X: -1, Y: -1},
25+
{X: 0, Y: 1},
26+
},
27+
}
28+
29+
var startingPositions = map[byte]Coordinate{
30+
'I': {X: 3, Y: -1},
31+
'O': {X: 4, Y: -2},
32+
'6': {X: 3, Y: -2},
33+
}
34+
35+
var tetriminos = []Tetrimino{
36+
{
37+
Value: 'I',
38+
Cells: [][]bool{
39+
{true, true, true, true},
40+
},
41+
Pos: startingPositions['I'],
42+
RotationCoords: RotationCoords['I'],
43+
},
44+
{
45+
Value: 'O',
46+
Cells: [][]bool{
47+
{true, true},
48+
{true, true},
49+
},
50+
Pos: startingPositions['O'],
51+
RotationCoords: RotationCoords['O'],
52+
},
53+
{
54+
Value: 'T',
55+
Cells: [][]bool{
56+
{false, true, false},
57+
{true, true, true},
58+
},
59+
Pos: startingPositions['6'],
60+
RotationCoords: RotationCoords['6'],
61+
},
62+
{
63+
Value: 'S',
64+
Cells: [][]bool{
65+
{false, true, true},
66+
{true, true, false},
67+
},
68+
Pos: startingPositions['6'],
69+
RotationCoords: RotationCoords['6'],
70+
},
71+
{
72+
Value: 'Z',
73+
Cells: [][]bool{
74+
{true, true, false},
75+
{false, true, true},
76+
},
77+
Pos: startingPositions['6'],
78+
RotationCoords: RotationCoords['6'],
79+
},
80+
{
81+
Value: 'J',
82+
Cells: [][]bool{
83+
{true, false, false},
84+
{true, true, true},
85+
},
86+
Pos: startingPositions['6'],
87+
RotationCoords: RotationCoords['6'],
88+
},
89+
{
90+
Value: 'L',
91+
Cells: [][]bool{
92+
{false, false, true},
93+
{true, true, true},
94+
},
95+
Pos: startingPositions['6'],
96+
RotationCoords: RotationCoords['6'],
97+
},
98+
}
99+
100+
type bag struct {
101+
elements []*Tetrimino
102+
playfieldHeight int
103+
}
104+
105+
func defaultBag(playfieldHeight int) *bag {
106+
b := bag{
107+
elements: make([]*Tetrimino, 0, 14),
108+
playfieldHeight: playfieldHeight,
109+
}
110+
b.fillBag()
111+
b.fillBag()
112+
return &b
113+
}
114+
115+
func (b *bag) next() *Tetrimino {
116+
tet := b.elements[0]
117+
b.elements = b.elements[1:]
118+
119+
if len(b.elements) <= 7 {
120+
b.fillBag()
121+
}
122+
123+
tet.Pos.Y += b.playfieldHeight - 20
124+
return tet
125+
}
126+
127+
func (b *bag) fillBag() {
128+
perm := rand.Perm(len(tetriminos))
129+
for _, i := range perm {
130+
if len(b.elements) == 14 {
131+
return
132+
}
133+
b.enqueue(&tetriminos[i])
134+
}
135+
}
136+
137+
// Adds an item to the end of the queue
138+
func (b *bag) enqueue(value *Tetrimino) {
139+
b.elements = append(b.elements, value)
140+
}
141+
142+
// Removes an item from the front of the queue and returns it
143+
// If the queue is empty, returns 0 and an error
144+
func (b *bag) dequeue() (*Tetrimino, error) {
145+
if len(b.elements) == 0 {
146+
return nil, fmt.Errorf("queue is empty")
147+
}
148+
element := b.elements[0] // Get the first element
149+
b.elements = b.elements[1:] // Remove it from the queue
150+
return element, nil
151+
}

model.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type Model struct {
2121
holdTet *Tetrimino
2222
fall *Fall
2323
scoring *scoring
24+
bag *bag
2425
}
2526

2627
type Fall struct {
@@ -69,8 +70,13 @@ func InitialModel() *Model {
6970
Value: 0,
7071
},
7172
}
73+
m.bag = defaultBag(len(m.playfield))
7274
m.fall = defaultFall(m.scoring.level)
73-
m.currentTet = m.playfield.NewTetrimino()
75+
m.currentTet = m.bag.next()
76+
err := m.playfield.AddTetrimino(m.currentTet)
77+
if err != nil {
78+
panic(fmt.Errorf("failed to add tetrimino to playfield: %w", err))
79+
}
7480
return m
7581
}
7682

@@ -112,7 +118,11 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
112118
if !m.currentTet.canMoveDown(m.playfield) {
113119
action := m.playfield.removeCompletedLines(m.currentTet)
114120
m.scoring.processAction(action)
115-
m.currentTet = m.playfield.NewTetrimino()
121+
m.currentTet = m.bag.next()
122+
err := m.playfield.AddTetrimino(m.currentTet)
123+
if err != nil {
124+
panic(fmt.Errorf("failed to add tetrimino to playfield: %w", err))
125+
}
116126
break
117127
}
118128

@@ -128,7 +138,11 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
128138
if !m.currentTet.canMoveDown(m.playfield) {
129139
action := m.playfield.removeCompletedLines(m.currentTet)
130140
m.scoring.processAction(action)
131-
m.currentTet = m.playfield.NewTetrimino()
141+
m.currentTet = m.bag.next()
142+
err := m.playfield.AddTetrimino(m.currentTet)
143+
if err != nil {
144+
panic(fmt.Errorf("failed to add tetrimino to playfield: %w", err))
145+
}
132146
break
133147
}
134148
err := m.currentTet.MoveDown(&m.playfield)

playfield.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func (p *Playfield) removeCells(tetrimino *Tetrimino) error {
3434
return nil
3535
}
3636

37-
func (p *Playfield) addCells(tetrimino *Tetrimino) error {
37+
func (p *Playfield) AddTetrimino(tetrimino *Tetrimino) error {
3838
for row := range tetrimino.Cells {
3939
for col := range tetrimino.Cells[row] {
4040
if tetrimino.Cells[row][col] {
@@ -48,12 +48,6 @@ func (p *Playfield) addCells(tetrimino *Tetrimino) error {
4848
return nil
4949
}
5050

51-
func (p *Playfield) NewTetrimino() *Tetrimino {
52-
tet := RandomTetrimino(len(p))
53-
p.addCells(tet)
54-
return tet
55-
}
56-
5751
func (p *Playfield) removeCompletedLines(tet *Tetrimino) action {
5852
lines := 0
5953
for row := range tet.Cells {

tetrimino.go

Lines changed: 4 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2,101 +2,8 @@ package main
22

33
import (
44
"fmt"
5-
"math/rand"
65
)
76

8-
var RotationCoords = map[byte][]Coordinate{
9-
'I': {
10-
{X: -1, Y: -1},
11-
{X: 2, Y: 1},
12-
{X: -2, Y: -2},
13-
{X: 1, Y: 2},
14-
},
15-
'O': {
16-
{X: 0, Y: 0},
17-
{X: 0, Y: 0},
18-
{X: 0, Y: 0},
19-
{X: 0, Y: 0},
20-
},
21-
'6': { // All tetriminos with 6 cells (T, S, Z, J, L)
22-
{X: 0, Y: 0},
23-
{X: 1, Y: 0},
24-
{X: -1, Y: -1},
25-
{X: 0, Y: 1},
26-
},
27-
}
28-
29-
var startingPositions = map[byte]Coordinate{
30-
'I': {X: 3, Y: -1},
31-
'O': {X: 4, Y: -2},
32-
'6': {X: 3, Y: -2},
33-
}
34-
35-
var tetriminos = []Tetrimino{
36-
{
37-
Value: 'I',
38-
Cells: [][]bool{
39-
{true, true, true, true},
40-
},
41-
Pos: startingPositions['I'],
42-
RotationCoords: RotationCoords['I'],
43-
},
44-
{
45-
Value: 'O',
46-
Cells: [][]bool{
47-
{true, true},
48-
{true, true},
49-
},
50-
Pos: startingPositions['O'],
51-
RotationCoords: RotationCoords['O'],
52-
},
53-
{
54-
Value: 'T',
55-
Cells: [][]bool{
56-
{false, true, false},
57-
{true, true, true},
58-
},
59-
Pos: startingPositions['6'],
60-
RotationCoords: RotationCoords['6'],
61-
},
62-
{
63-
Value: 'S',
64-
Cells: [][]bool{
65-
{false, true, true},
66-
{true, true, false},
67-
},
68-
Pos: startingPositions['6'],
69-
RotationCoords: RotationCoords['6'],
70-
},
71-
{
72-
Value: 'Z',
73-
Cells: [][]bool{
74-
{true, true, false},
75-
{false, true, true},
76-
},
77-
Pos: startingPositions['6'],
78-
RotationCoords: RotationCoords['6'],
79-
},
80-
{
81-
Value: 'J',
82-
Cells: [][]bool{
83-
{true, false, false},
84-
{true, true, true},
85-
},
86-
Pos: startingPositions['6'],
87-
RotationCoords: RotationCoords['6'],
88-
},
89-
{
90-
Value: 'L',
91-
Cells: [][]bool{
92-
{false, false, true},
93-
{true, true, true},
94-
},
95-
Pos: startingPositions['6'],
96-
RotationCoords: RotationCoords['6'],
97-
},
98-
}
99-
1007
type Coordinate struct {
1018
X, Y int
1029
}
@@ -109,15 +16,6 @@ type Tetrimino struct {
10916
RotationCoords []Coordinate
11017
}
11118

112-
// TODO: update to be private. Change tests to be on NewTetrimino instead.
113-
// randomTetrimino returns a random tetrimino.
114-
// The tetrimino will be positioned just above the playfield.
115-
func RandomTetrimino(playfieldHeight int) *Tetrimino {
116-
t := tetriminos[rand.Intn(len(tetriminos))]
117-
t.Pos.Y += playfieldHeight - 20
118-
return &t
119-
}
120-
12119
// MoveDown moves the tetrimino down one row.
12220
// If the tetrimino cannot move down, it will be added to the playfield and a new tetrimino will be returned.
12321
func (t *Tetrimino) MoveDown(playfield *Playfield) error {
@@ -126,7 +24,7 @@ func (t *Tetrimino) MoveDown(playfield *Playfield) error {
12624
return fmt.Errorf("failed to remove cells: %w", err)
12725
}
12826
t.Pos.Y++
129-
err = playfield.addCells(t)
27+
err = playfield.AddTetrimino(t)
13028
if err != nil {
13129
return fmt.Errorf("failed to add cells: %w", err)
13230
}
@@ -144,7 +42,7 @@ func (t *Tetrimino) MoveLeft(playfield *Playfield) error {
14442
return fmt.Errorf("failed to remove cells: %w", err)
14543
}
14644
t.Pos.X--
147-
err = playfield.addCells(t)
45+
err = playfield.AddTetrimino(t)
14846
if err != nil {
14947
return fmt.Errorf("failed to add cells: %w", err)
15048
}
@@ -162,7 +60,7 @@ func (t *Tetrimino) MoveRight(playfield *Playfield) error {
16260
return fmt.Errorf("failed to remove cells: %w", err)
16361
}
16462
t.Pos.X++
165-
err = playfield.addCells(t)
63+
err = playfield.AddTetrimino(t)
16664
if err != nil {
16765
return fmt.Errorf("failed to add cells: %w", err)
16866
}
@@ -239,7 +137,7 @@ func (t *Tetrimino) Rotate(playfield *Playfield, clockwise bool) error {
239137
t.Cells = rotated.Cells
240138
}
241139

242-
err = playfield.addCells(t)
140+
err = playfield.AddTetrimino(t)
243141
if err != nil {
244142
return fmt.Errorf("failed to add cells: %w", err)
245143
}

0 commit comments

Comments
 (0)