aboutsummaryrefslogtreecommitdiff
path: root/tris/move.go
blob: 927645f2f1c8cf2db57d67e02f74ed3d4341ee03 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package tris

// As at the bottom of https://tetris.fandom.com/wiki/SRS but Y inverse because we start Y at the top
type Kicks map[Rotation][]Point

// Kics for all pieces except I and O pieces
var K3CW = Kicks{
	0: {{X:0, Y:0}, {X:-1, Y:0}, {X:-1, Y: -1}, {X:0, Y:2}, {X:-1, Y:2}},
	1: {{X:0, Y:0}, {X:1, Y:0}, {X:1, Y: 1}, {X:0, Y:-2}, {X:1, Y:-2}},
	2: {{X:0, Y:0}, {X:1, Y:0}, {X:1, Y: -1}, {X:0, Y:2}, {X:1, Y:2}},
	3: {{X:0, Y:0}, {X:-1, Y:0}, {X:-1, Y: 1}, {X:0, Y:-2}, {X:-1, Y:-2}},
}

// Long Bar (I piece) Kicks
var LCW = Kicks{
	0: {{X:0, Y:0}, {X:-2, Y:0}, {X:1, Y: 0}, {X:-2, Y:1}, {X:1, Y:-2}},
	1: {{X:0, Y:0}, {X:-1, Y:0}, {X:2, Y: 0}, {X:-1, Y:-2}, {X:2, Y:1}},
	2: {{X:0, Y:0}, {X:2, Y:0}, {X:-1, Y: 0}, {X:2, Y:-1}, {X:-1, Y:2}},
	3: {{X:0, Y:0}, {X:1, Y:0}, {X:-2, Y: 0}, {X:1, Y:2}, {X:-2, Y:-1}},
}

// Try all possible kicks
func (ks Kicks) Try(p, np Placement, f Field) Placement {
	direction := (np.Rot - p.Rot + 4) % 4
	rot := np.Rot
	factor := 1
	if direction == Spawn {
		factor = 0
	}
	if direction == CounterClockwise {
		rot = (rot + 1) % 4
		factor = -1
	}
	x, y := np.X, np.Y
	for _, k := range ks[rot] {
		np.X, np.Y = x + k.X * factor, y + k.Y * factor
		if !np.Collide(f) {
			return np
		}
	}
	return p
}

// Floor checks if the piece touches something underneath it
func (p Placement) Floor(f Field) bool {
	fp := p
	fp.Y++
	return fp.Collide(f)
}

// Move tries the move indicated by the parameters, applies kicks to fix when it doesn't work,
// and returns if the piece is on the floor or topping out
func (p Placement) Move(f Field, rot Rotation, x, y int) (np Placement, floor, topout bool) {
	np = p
	np.Rot = rot
	np.X = x
	np.Y = y
	if !np.Collide(f) { // free air
		return np, np.Floor(f), false
	}
	// start handling kicks
	switch p.piece {
	case IPiece:
		np = LCW.Try(p, np, f)
	case JPiece, SPiece, ZPiece, TPiece, LPiece:
		np = K3CW.Try(p, np, f)
	default:
		np = p
	}

	if np.Floor(f) && np.Y < 0 {
		return np, true, true
	}
	return np, np.Floor(f), false
}