package tris import ( "math/rand" "time" ) type Point struct { X, Y int } func (p Point) Add(q Point) Point { return Point{X: p.X+q.X, Y: p.Y+q.Y} } 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 time.Time } func (p Placement) Collide(f Field) bool { pf, ok := p.Field() for x := 0; x < 10; x++ { for y := 0; y < 20; y++ { if f[y][x] && pf[y][x] { return true } } } 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 = time.Second/2 var ReferenceBag = Bag{IPiece, JPiece, LPiece, OPiece, SPiece, TPiece, ZPiece} func NewBag() Bag { b := ReferenceBag.Randomize() return b } func (b Bag) Randomize() Bag { rand.Shuffle(len(b), func(i, j int) { b[i], b[j] = b[j], b[i] }) return b } func (b Bag) Pick() (Bag, Placement) { if len(b) == 0 { b = NewBag() } piece := Placement{piece: b[0], X: 3, Y: -2, Lock: time.Now()} b = b[1:] return b, 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) { for _, row := range f { for _, block := range row { if block { output += "\u2588\u2589" } else { output += "\u2591\u2591" } } output += "|\n" } return output }