Snake.cpp 5.57 KB
Newer Older
1
2
#include "config.h"

3
4
#include "Field.h"

Thomas Kolb's avatar
Thomas Kolb committed
5
6
#include "Snake.h"

7
Snake::Snake(Field *field)
8
	: m_field(field), m_mass(1.0f), m_heading(0.0f)
Thomas Kolb's avatar
Thomas Kolb committed
9
{
10
	m_segments.emplace_back(Vector2D {0,0});
Thomas Kolb's avatar
Thomas Kolb committed
11

12
	ensureSizeMatchesMass();
Thomas Kolb's avatar
Thomas Kolb committed
13
14
}

Hubert Denkmair's avatar
Hubert Denkmair committed
15
16
Snake::Snake(Field *field, const Vector2D &startPos, real_t start_mass,
		real_t start_heading)
17
	: m_field(field), m_mass(start_mass), m_heading(start_heading)
Thomas Kolb's avatar
Thomas Kolb committed
18
{
Thomas Kolb's avatar
Thomas Kolb committed
19
	// create the first segment manually
20
	m_segments.emplace_back(startPos);
Thomas Kolb's avatar
Thomas Kolb committed
21
22

	// create the other segments
23
	ensureSizeMatchesMass();
Thomas Kolb's avatar
Thomas Kolb committed
24
25
}

26
void Snake::ensureSizeMatchesMass(void)
Thomas Kolb's avatar
Thomas Kolb committed
27
{
Thomas Kolb's avatar
Thomas Kolb committed
28
29
30
31
	m_targetSegmentDistance = pow(
			m_mass * config::SNAKE_SEGMENT_DISTANCE_FACTOR,
			config::SNAKE_SEGMENT_DISTANCE_EXPONENT);

Thomas Kolb's avatar
Thomas Kolb committed
32
	std::size_t curLen = m_segments.size();
Thomas Kolb's avatar
Thomas Kolb committed
33
	std::size_t targetLen = static_cast<std::size_t>(
34
			m_mass / m_targetSegmentDistance / 5);
Thomas Kolb's avatar
Thomas Kolb committed
35
36
			//pow(m_mass, config::SNAKE_NSEGMENTS_EXPONENT));

Thomas Kolb's avatar
Thomas Kolb committed
37

Thomas Kolb's avatar
Thomas Kolb committed
38
39
40
41
42
43
44
45
	// ensure there are at least 2 segments to define movement direction
	if(targetLen < 2) {
		targetLen = 2;
	}

	if(curLen < targetLen) {
		// segments have to be added:
		// repeat the last segment until the new target length is reached
46
		const Segment &refSegment = m_segments[curLen-1];
Thomas Kolb's avatar
Thomas Kolb committed
47
		for(std::size_t i = 0; i < (targetLen - curLen); i++) {
48
			m_segments.emplace_back(refSegment);
Thomas Kolb's avatar
Thomas Kolb committed
49
50
51
52
		}
	} else if(curLen > targetLen) {
		// segments must be removed
		m_segments.resize(targetLen);
Thomas Kolb's avatar
Thomas Kolb committed
53
	}
54
55

	// update segment radius
Thomas Kolb's avatar
Thomas Kolb committed
56
57
	m_segmentRadius = std::pow((20*m_mass+100), 0.3) - 3.9810717055349722;
	//                                    100**0.3 --------^
Thomas Kolb's avatar
Thomas Kolb committed
58
59
}

Hubert Denkmair's avatar
Hubert Denkmair committed
60
real_t Snake::maxRotationPerStep(void)
Thomas Kolb's avatar
Thomas Kolb committed
61
62
{
	// TODO: make this better?
Thomas Kolb's avatar
Thomas Kolb committed
63
	return 10.0 / (m_segmentRadius/10.0 + 1);
Thomas Kolb's avatar
Thomas Kolb committed
64
65
}

Hubert Denkmair's avatar
Hubert Denkmair committed
66
void Snake::consume(const Food& food)
Thomas Kolb's avatar
Thomas Kolb committed
67
{
Hubert Denkmair's avatar
Hubert Denkmair committed
68
	m_mass += food.getValue();
69
	ensureSizeMatchesMass();
Thomas Kolb's avatar
Thomas Kolb committed
70
71
}

Hubert Denkmair's avatar
Hubert Denkmair committed
72
std::size_t Snake::move(real_t targetAngle, bool boost)
Thomas Kolb's avatar
Thomas Kolb committed
73
74
{
	// calculate delta angle
Hubert Denkmair's avatar
Hubert Denkmair committed
75
	real_t deltaAngle = targetAngle - m_heading;
Thomas Kolb's avatar
Thomas Kolb committed
76
77
78
79
80
81
82
83
84

	// normalize delta angle
	if(deltaAngle > 180) {
		deltaAngle -= 360;
	} else if(deltaAngle < -180) {
		deltaAngle += 360;
	}

	// limit rotation rate
Hubert Denkmair's avatar
Hubert Denkmair committed
85
	real_t maxDelta = maxRotationPerStep();
Thomas Kolb's avatar
Thomas Kolb committed
86
87
88
89
90
91
92
93
	if(deltaAngle > maxDelta) {
		deltaAngle = maxDelta;
	} else if(deltaAngle < -maxDelta) {
		deltaAngle = -maxDelta;
	}

	std::size_t oldSize = m_segments.size();

Thomas Kolb's avatar
Thomas Kolb committed
94
95
	// unwrap all coordinates
	for(std::size_t i = 0; i < m_segments.size(); i++) {
96
97
		auto &ref = (i == 0) ? m_segments[0].pos() : m_segments[i-1].pos();
		m_segments[i].setPos(m_field->unwrapCoords(m_segments[i].pos(), ref));
Thomas Kolb's avatar
Thomas Kolb committed
98
99
100
	}

	// remove the head from the segment list (will be re-added later)
101
	Segment headSegment(m_segments[0]);
Thomas Kolb's avatar
Thomas Kolb committed
102
103
	m_segments.erase(m_segments.begin());

Thomas Kolb's avatar
Thomas Kolb committed
104
105
106
	// create multiple segments while boosting
	std::size_t steps = 1;
	if(boost) {
107
		steps = config::SNAKE_BOOST_STEPS;
Thomas Kolb's avatar
Thomas Kolb committed
108
109
110
111
	}

	// create new segments at head
	for(std::size_t i = 0; i < steps; i++) {
112
113
		// calculate new segment offset
		m_heading += deltaAngle;
Thomas Kolb's avatar
Thomas Kolb committed
114

Hubert Denkmair's avatar
Hubert Denkmair committed
115
		real_t headingRad = m_heading * M_PI / 180;
116
117
118
		Vector2D movementVector2D(cos(headingRad), sin(headingRad));
		movementVector2D *= config::SNAKE_DISTANCE_PER_STEP;

119
		headSegment.setPos(headSegment.pos() + movementVector2D);
Thomas Kolb's avatar
Thomas Kolb committed
120
121
122
123
124
125

		m_movedSinceLastSpawn += config::SNAKE_DISTANCE_PER_STEP;

		// create new segments, if necessary
		while(m_movedSinceLastSpawn > m_targetSegmentDistance) {
			// vector from the first segment to the direction of the head
126
			Vector2D newSegmentOffset = headSegment.pos() - m_segments[0].pos();
127
			newSegmentOffset *= (m_targetSegmentDistance / newSegmentOffset.norm());
Thomas Kolb's avatar
Thomas Kolb committed
128
129

			m_movedSinceLastSpawn -= m_targetSegmentDistance;
Thomas Kolb's avatar
Thomas Kolb committed
130

Thomas Kolb's avatar
Thomas Kolb committed
131
			// create new segment
132
			m_segments.emplace_front(m_segments[0].pos() + newSegmentOffset);
Thomas Kolb's avatar
Thomas Kolb committed
133
		}
Thomas Kolb's avatar
Thomas Kolb committed
134
135
	}

Thomas Kolb's avatar
Thomas Kolb committed
136
137
138
	// re-add head
	m_segments.push_front(headSegment);

139
140
141
142
143
144
145
	// normalize heading
	if(m_heading > 180) {
		m_heading -= 360;
	} else if(m_heading < -180) {
		m_heading += 360;
	}

Thomas Kolb's avatar
Thomas Kolb committed
146
147
	// force size to previous size (removes end segments)
	m_segments.resize(oldSize);
148

Thomas Kolb's avatar
Thomas Kolb committed
149
150
	// pull-together effect
	for(std::size_t i = 1; i < m_segments.size()-1; i++) {
151
152
153
		m_segments[i].setPos(
			m_segments[i].pos() * (1 - config::SNAKE_PULL_FACTOR) +
			(m_segments[i+1].pos() * 0.5 + m_segments[i-1].pos() * 0.5) * config::SNAKE_PULL_FACTOR
154
		);
Thomas Kolb's avatar
Thomas Kolb committed
155
156
157
158
	}

	// wrap coordinates
	for(std::size_t i = 0; i < m_segments.size(); i++) {
159
		m_segments[i].setPos(m_field->wrapCoords(m_segments[i].pos()));
Thomas Kolb's avatar
Thomas Kolb committed
160
161
	}

162
163
	m_boostedLastMove = boost;

Thomas Kolb's avatar
Thomas Kolb committed
164
	return m_segments.size(); // == number of new segments at head
Thomas Kolb's avatar
Thomas Kolb committed
165
}
166

167
const Snake::SegmentList& Snake::getSegments(void) const
168
169
170
{
	return m_segments;
}
Thomas Kolb's avatar
Thomas Kolb committed
171

172
const Vector2D& Snake::getHeadPosition(void) const
Thomas Kolb's avatar
Thomas Kolb committed
173
{
174
	return m_segments[0].pos();
Thomas Kolb's avatar
Thomas Kolb committed
175
}
176

Hubert Denkmair's avatar
Hubert Denkmair committed
177
real_t Snake::getSegmentRadius(void) const
178
179
180
{
	return m_segmentRadius;
}
181

Hubert Denkmair's avatar
Hubert Denkmair committed
182
bool Snake::canConsume(const Food &food)
183
{
184
	const Vector2D &headPos = m_segments[0].pos();
Hubert Denkmair's avatar
Hubert Denkmair committed
185
	const Vector2D &foodPos = food.pos();
186

187
	Vector2D unwrappedFoodPos = m_field->unwrapCoords(foodPos, headPos);
Hubert Denkmair's avatar
Hubert Denkmair committed
188
	real_t maxRange = getConsumeRadius();
189

Thomas Kolb's avatar
Thomas Kolb committed
190
	// range check
191
	return (headPos - unwrappedFoodPos).squaredNorm() < (maxRange*maxRange);
192
}
193

194
void Snake::convertToFood(const std::shared_ptr<Bot> &hunter) const
195
{
Hubert Denkmair's avatar
Hubert Denkmair committed
196
	real_t foodPerSegment = m_mass / m_segments.size() * config::SNAKE_CONVERSION_FACTOR;
197
198

	for(auto &s: m_segments) {
199
		m_field->createDynamicFood(foodPerSegment, s.pos(), m_segmentRadius, hunter);
200
201
	}
}
Hubert Denkmair's avatar
Hubert Denkmair committed
202

203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
void Snake::dropFood(float_t value)
{
	Vector2D dropOffset = (m_segments.end() - 1)->pos() - (m_segments.end() - 2)->pos();
	Vector2D dropPos = (m_segments.end() - 1)->pos() + dropOffset.normalized() * 5;

	m_field->createDynamicFood(value * config::SNAKE_CONVERSION_FACTOR, dropPos, m_segmentRadius, nullptr);
	m_mass -= value;

	if(m_mass < 1e-6) {
		m_mass = 1e-6;
	}

	ensureSizeMatchesMass();
}

Hubert Denkmair's avatar
Hubert Denkmair committed
218
219
220
221
real_t Snake::getConsumeRadius()
{
	return m_segmentRadius * config::SNAKE_CONSUME_RANGE;
}