This repository has been archived by the owner on Oct 20, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
World.cpp
145 lines (125 loc) · 4.55 KB
/
World.cpp
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include "World.hpp"
#include <algorithm>
#include <stdexcept>
namespace {
// Make an element where all cells are alive, and no other bits are set.
constexpr World::element_type make_all_one_mask() {
World::element_type mask = 1;
for (size_t i = 0; i < World::cells_per_element; ++i)
mask |= mask << World::bits_per_cell;
return mask;
}
constexpr World::element_type all_one_mask = make_all_one_mask();
}
World::World() : m_width(), m_height(), m_active(), m_future()
{ }
World::World(size_t width, size_t height)
: m_width(width/cells_per_element), m_height(height),
m_active(m_width*(m_height+2)), m_future(m_active)
{
test_size(width, height);
}
World::World(BaseWorld const& world) : World() {
copy(world);
}
void World::update() {
// Cells are stored as 0001s (alive) and 0000s (dead). By adding these values, we can quickly
// get a count of the number of neighbours a cell has. If the cell is alive, we take the
// bitwise complement of the resulting value; we then check whether the resulting bit pattern
// leaves the cell alive (this is unambiguous) and store that.
element_type* in = m_active.data() + m_width;
element_type* out = m_future.data() + m_width;
element_type old_neighbours = 0;
element_type neighbours = 0;
element_type new_neighbours = 0;
auto process_one = [&] {
element_type in_value = *in << 3;
// We need the old and new neighbour counts in order to get them right for the leftmost
// and rightmost cell.
element_type total_neighbours = (old_neighbours >> max_shift)
+ (neighbours << bits_per_cell)
+ neighbours
+ (neighbours >> bits_per_cell)
+ (new_neighbours << max_shift);
element_type result = total_neighbours ^ in_value;
element_type result_1 = result >> 1;
element_type result_2 = result >> 2;
element_type result_3 = result >> 3;
element_type sub = ~result_2 & result_1 & result;
element_type option_a = ~result_3 & sub;
element_type option_b = result_3 & sub;
element_type option_c = result_3 & result_2 & ~result_1 & ~result;
*out = (option_a | option_b | option_c) & all_one_mask;
++in;
++out;
old_neighbours = neighbours;
neighbours = new_neighbours;
};
for (size_t j = 0; j < m_height; ++j) {
auto f = [&](int i) { return in[-m_width+i] + in[i] + in[m_width+i]; };
old_neighbours = 0;
neighbours = f(0);
size_t i = 0;
goto middle;
while (i < m_width/unroll_factor) {
new_neighbours = f(1);
process_one();
middle:
new_neighbours = f(1);
process_one();
new_neighbours = f(1);
process_one();
new_neighbours = f(1);
process_one();
new_neighbours = f(1);
process_one();
new_neighbours = f(1);
process_one();
new_neighbours = f(1);
process_one();
new_neighbours = f(1);
process_one();
++i;
}
new_neighbours = 0;
process_one();
};
m_active.swap(m_future);
}
void World::resize(size_t width, size_t height) {
test_size(width, height);
m_width = width/cells_per_element;
m_height = height;
m_active.resize(m_width*(m_height+2));
m_future.resize(m_width*(m_height+2));
}
size_t World::width() const {
return cells_per_element*m_width;
}
size_t World::height() const {
return m_height;
}
Cell World::get(size_t x, size_t y) const {
auto ix = index(x, y);
auto jx = bits_per_cell * (x % cells_per_element);
return to_cell(element_type(1) & (m_active[ix] >> jx));
}
void World::set(size_t x, size_t y, Cell cell) {
auto ix = index(x, y);
auto jx = bits_per_cell * (x % cells_per_element);
if (cell == alive)
m_active[ix] |= element_type(1u) << jx;
else
m_active[ix] &= ~(element_type(1u) << jx);
}
size_t World::index(size_t x, size_t y) const {
return (y+1) * m_width + (x / cells_per_element);
}
void World::test_size(size_t width, size_t height) const {
(void)height; // all heights are fine at the moment
// This is somewhat redundant, as cells_per_element is larger than 2.
if (width < 2)
throw std::runtime_error{"Width must be at least two."};
if (width % (cells_per_element * 8) != 0)
throw std::runtime_error{"Width must be a multiple of cells_per_element times unroll_factor."};
}