aboutsummaryrefslogtreecommitdiff
path: root/tris/move.go
blob: 127ce06a9c2ab14b04746af53920beff1d7c18e9 (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
76
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

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}},
}

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}},
}

func (ks Kicks) Try(rot Rotation, reverse bool, p Placement, f Field) (np Placement) {
	orot := (rot + 3)%4
	if reverse { // for going backwards, you start from one more
		rot--
		orot = (rot + 1)%4
	}
	for _, k := range ks[rot] {
		np = p
		x, y := k.X, k.Y
		if reverse {
			x, y = -x, -y
		}
		np.X, np.Y = p.X+x, p.Y+y
		if !np.Collide(f) {
			return np
		}
	}
	p.Rot = orot
	return p
}

func (p Placement) Floor(f Field) bool {
	fp := p
	fp.Y++
	return fp.Collide(f)
}

// CW checks if the rotation from r to nr is a Clockwise rotation
func (r Rotation) CW(nr Rotation) bool {
	r += 4
	return (r - nr)%4 == 1 // if nr is 1 modulo 4 higher than r, it is a CW rotation
}

// 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(rot, np.Rot.CW(rot), np, f)
	case JPiece, SPiece, ZPiece, TPiece, LPiece:
		np = K3CW.Try(rot, np.Rot.CW(rot), np, f)
	}

	np = p // last resort reset p
	if np.Floor(f) && np.Y < 0 {
		return np, true, true
	}
	return np, np.Floor(f), false
}