package tris type Point struct { X, Y int } type Piece [4]uint16 // A table that represents each piece from https://tetris.fandom.com/wiki/SRS // in binary starting with 1 top left going per row, here in hexadecimal shorthand. var ( IPiece = Piece{0x0F00, 0x4444, 0x00F0, 0x2222} JPiece = Piece{0x1700, 0x6220, 0x0740, 0x2230} LPiece = Piece{0x4700, 0x2260, 0x0710, 0x3220} OPiece = Piece{0x6600, 0x6600, 0x6600, 0x6600} SPiece = Piece{0x6300, 0x2640, 0x0630, 0x1320} TPiece = Piece{0x2700, 0x2620, 0x0720, 0x2320} ZPiece = Piece{0x3600, 0x4620, 0x0360, 0x2310} ) type Rotation int const ( Spawn Rotation = iota Clockwise Flip CounterClockwise ) type Placement struct { piece Piece X int Y int Rot Rotation Lock int } func (p *Placement) Drop(f Field) bool { newp := p newp.Y++ if !newp.Collide(f) { p = newp return true } return false } func (p Placement) Collide(f Field) bool { pf, ok := p.Field() for x := 0; x < 9; x++ { for y := 0; y < 19; y++ { if f[y][x] && pf[y][x] { return false } } } return ok } func (p Placement) Field() (Field, bool) { var f Field ok := true for _, point := range p.Points() { if point.X < 0 || point.X > 9 || point.Y > 19 { ok = false continue } if point.Y < 0 { // above the playing field we count the piece as on the board continue } f[point.Y][point.X] = true } return f, ok } func (p Placement) Points() []Point { piece := p.piece[p.Rot] var points []Point x := []int{0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3} y := []int{0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3} for bm, i := uint16(0x8000), 0; i < 16; bm, i = bm >> 1, i + 1 { if piece&bm == bm { points = append(points, Point{X: p.X + x[i], Y: p.Y + y[i]}) } } return points } type Bag []Piece var LockDelay = 30 var ReferenceBag = Bag{IPiece, JPiece, LPiece, OPiece, SPiece, TPiece, ZPiece} func NewBag() *Bag { b := ReferenceBag.Randomize() return &b } func (b Bag) Randomize() Bag { //TODO: implement randomizer return b } func (b *Bag) Pick() Placement { if len(*b) == 0 { b = NewBag() } piece := Placement{piece: (*b)[0], X: 3, Y: -2, Lock: LockDelay} *b = (*b)[1:] return piece } type Field [20][10]bool func (f Field) Add(p Placement) Field { fn := f for _, point := range p.Points() { if point.Y < 0 || point.Y > 19 || point.X < 0 || point.X > 9 { continue } fn[point.Y][point.X] = true } return fn } func (f Field) String() (output string) { var toprow [10]bool var top bool for _, row := range f { top = !top for i, block := range row { if top { toprow[i] = block continue } switch { case toprow[i] && block: output += "\u2588" case toprow[i] && !block: output += "\u2580" case !toprow[i] && block: output += "\u2584" default: output += " " } } if !top { output += "\n" } } return output }