12using namespace bicameral::multires;
41 uint32_t temperatureMilliKelvin) {
45 command.
type =
static_cast<uint16_t
>(type);
49 SetPayload(command, lab::LAB_PAYLOAD_TEMPERATURE, temperatureMilliKelvin);
50 command.
size =
static_cast<uint16_t
>(
sizeof(uint32_t) * 2);
56 bool SameBytes(
const std::vector<T>& a,
const std::vector<T>& b) {
57 static_assert(std::is_trivially_copyable_v<T>);
58 return a.size() == b.size() && std::memcmp(a.data(), b.data(), a.size() *
sizeof(T)) == 0;
63 void Append(std::vector<std::byte>& bytes,
const T& value) {
64 static_assert(std::is_trivially_copyable_v<T>);
65 const auto* begin =
reinterpret_cast<const std::byte*
>(&value);
66 bytes.insert(bytes.end(), begin, begin +
sizeof(T));
70 bool Take(std::span<const std::byte>& bytes, T& value) {
71 static_assert(std::is_trivially_copyable_v<T>);
72 if (bytes.size() <
sizeof(T))
75 std::memcpy(&value, bytes.data(),
sizeof(T));
76 bytes = bytes.subspan(
sizeof(T));
86 std::span<const SpeciesAmount> contents, uint32_t temperatureMilliKelvin) {
87 Command command = MakeLabCommand(targetTick, sequence, lab::LAB_COMMAND_TYPE_FILL, cell,
88 temperatureMilliKelvin);
91 const auto count =
static_cast<uint32_t
>(contents.size());
92 SetPayload(command, lab::LAB_PAYLOAD_COUNT, count);
93 const uint32_t written = std::min(count, lab::LAB_MAX_FILL_SPECIES);
94 for (uint32_t i = 0; i < written; ++i) {
95 const uint32_t word = lab::LAB_PAYLOAD_ENTRIES + (COMMAND_WORDS_PER_ENTRY * i);
96 SetPayload(command, word, contents[i].species);
97 SetPayload(command, word + 1,
static_cast<uint32_t
>(contents[i].amount));
98 SetPayload(command, word + 2,
static_cast<uint32_t
>(contents[i].amount >> 32));
101 command.
size =
static_cast<uint16_t
>(
sizeof(uint32_t) *
102 (lab::LAB_PAYLOAD_ENTRIES + (COMMAND_WORDS_PER_ENTRY * written)));
108 uint32_t temperatureMilliKelvin) {
109 return MakeLabCommand(targetTick, sequence, lab::LAB_COMMAND_TYPE_TEMPERATURE, cell, temperatureMilliKelvin);
114 lab::LabCommand words{};
115 std::memcpy(&words, &command,
sizeof(words));
123 const uint32_t oxygen = table.
SpeciesId(
"oxygen");
124 const uint32_t nitrogen = table.
SpeciesId(
"nitrogen");
125 const uint32_t cellulose = table.
SpeciesId(
"cellulose");
126 const uint32_t carbon = table.
SpeciesId(
"carbon");
127 const uint32_t carbonDioxide = table.
SpeciesId(
"carbon_dioxide");
129 std::vector<LabMaterial> materials;
130 const auto add = [&](std::string_view name, std::vector<SpeciesAmount> contents) {
131 const bool known = std::ranges::all_of(contents,
134 materials.push_back({.name = name, .contents = std::move(contents)});
137 add(
"空気", {{oxygen, AIR_OXYGEN_MICROMOLES}, {nitrogen, AIR_NITROGEN_MICROMOLES}});
138 add(
"木", {{cellulose, WOOD_CELLULOSE_MICROMOLES},
139 {oxygen, PORE_OXYGEN_MICROMOLES},
140 {nitrogen, PORE_NITROGEN_MICROMOLES}});
141 add(
"木炭", {{carbon, CHARCOAL_CARBON_MICROMOLES},
142 {oxygen, PORE_OXYGEN_MICROMOLES},
143 {nitrogen, PORE_NITROGEN_MICROMOLES}});
144 add(
"二酸化炭素", {{carbonDioxide, AIR_TOTAL_MICROMOLES}});
145 add(
"窒素", {{nitrogen, AIR_TOTAL_MICROMOLES}});
153 return {.worldBlocks = 1,
158 .ledgerColumns = 1 +
static_cast<uint32_t
>(table.
species.size()),
163 const std::array<SpeciesAmount, 2> air = {
170 std::vector<RxCell> cells(MR_BLOCK_CELLS, fill);
171 cells[0] = RxMakeEmptyCell(0);
188 const auto speciesCount =
static_cast<uint32_t
>(table.
species.size());
189 uint32_t applied = 0;
190 for (
const Command& command : commands) {
192 const uint32_t cell = lab::LabCommandCell(words, speciesCount);
193 if (cell == lab::LAB_NO_CELL)
197 target = lab::LabApplyCommand(view, target, words);
209 std::span<const Command> commands) {
221 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
227 mismatch.
cell = index;
230 mismatch.
what = std::format(
"刻み {} のセル ({}, {}, {}) が違う", tick, MrCellX(index), MrCellY(index),
236 std::string_view field =
"その他";
241 else if (!SameBytes(cpu.
ledger,
gpu.ledger))
248 mismatch.
what = std::format(
"刻み {} の{}が違う(セルは全部同じ)", tick, field);
255 std::vector<std::byte> bytes;
256 Append(bytes, RECORDING_MAGIC);
257 Append(bytes, RECORDING_VERSION);
260 Append(bytes,
static_cast<uint64_t
>(recording.
commands.size()));
261 Append(bytes,
static_cast<uint64_t
>(recording.
hashes.size()));
263 Append(bytes, command);
265 for (
const uint64_t hash : recording.
hashes)
272 std::array<char, 4> magic{};
273 uint32_t version = 0;
275 uint64_t commandCount = 0;
276 uint64_t hashCount = 0;
278 if (!Take(bytes, magic) || magic != RECORDING_MAGIC)
279 return std::unexpected(
"実験の記録ではない");
281 if (!Take(bytes, version) || version != RECORDING_VERSION)
282 return std::unexpected(std::format(
"記録の版 {} は読めない", version));
285 !Take(bytes, commandCount) || !Take(bytes, hashCount))
286 return std::unexpected(
"記録の見出しが壊れている");
288 if (bytes.size() != (commandCount *
COMMAND_BYTES) + (hashCount *
sizeof(uint64_t)) ||
290 return std::unexpected(
"記録の大きさが合わない");
292 recording.
commands.resize(commandCount);
294 (void)Take(bytes, command);
296 recording.
hashes.resize(hashCount);
297 for (uint64_t& hash : recording.
hashes)
298 (void)Take(bytes, hash);
302 return std::unexpected(
"記録のコマンドの並びが壊れている");
bool RxSameCell(const RxWideCell &a, const RxWideCell &b)
constexpr uint64_t AIR_OXYGEN_MICROMOLES
constexpr uint64_t WOOD_CELLULOSE_MICROMOLES
constexpr uint32_t COMMAND_WORDS_PER_ENTRY
constexpr uint32_t RECORDING_VERSION
constexpr uint64_t AIR_TOTAL_MICROMOLES
Command MakeLabCommand(uint64_t targetTick, uint32_t sequence, uint32_t type, LabCellPosition cell, uint32_t temperatureMilliKelvin)
constexpr uint64_t PORE_NITROGEN_MICROMOLES
constexpr uint64_t AIR_NITROGEN_MICROMOLES
constexpr uint64_t PORE_OXYGEN_MICROMOLES
bool SameBytes(const std::vector< T > &a, const std::vector< T > &b)
bool Take(std::span< const std::byte > &bytes, T &value)
constexpr std::array< char, 4 > RECORDING_MAGIC
constexpr uint64_t CHARCOAL_CARBON_MICROMOLES
void Append(std::vector< std::byte > &bytes, const T &value)
void SetPayload(Command &command, uint32_t word, uint32_t value)
void PokeBlock(MultiresNest &nest, uint32_t slot)
reaction::RxCell & CellAt(MultiresNest &nest, uint32_t slot, uint32_t index)
std::expected< LabRecording, std::string > ParseLabRecording(std::span< const std::byte > bytes)
constexpr bool CommandPrecedes(const Command &a, const Command &b)
constexpr MultiresStepOptions LAB_STEP_OPTIONS
RxCell LabBoxCell(const MultiresNest &nest, uint32_t index)
constexpr uint32_t LAB_BOX_SLOT
Command MakeLabFillCommand(uint64_t targetTick, uint32_t sequence, LabCellPosition cell, std::span< const SpeciesAmount > contents, uint32_t temperatureMilliKelvin)
MultiresNest MakeMultiresNest(const MultiresCapacity &capacity)
uint64_t HashWholeNest(const MultiresNest &nest)
std::vector< LabMaterial > MakeLabMaterials(const BakedReactionTable &table)
void StepLabBox(MultiresNest &nest, const BakedReactionTable &table, uint64_t tick, std::span< const Command > commands)
RxCell MakeReactionCell(const BakedReactionTable &table, std::span< const SpeciesAmount > amounts, int32_t temperatureMilliKelvin)
constexpr uint32_t COMMAND_BYTES
constexpr uint32_t LAB_BOX_EDGE
RxCell LoadNestCell(const MultiresNest &nest, uint32_t slot, uint32_t index)
Command MakeLabTemperatureCommand(uint64_t targetTick, uint32_t sequence, LabCellPosition cell, uint32_t temperatureMilliKelvin)
uint32_t ApplyLabCommands(MultiresNest &nest, const BakedReactionTable &table, std::span< const Command > commands)
MultiresNest MakeLabBoxNest(const BakedReactionTable &table)
constexpr uint64_t LAB_WORLD_SEED
uint32_t PlaceRootBlock(MultiresNest &nest, int64_t originX, int64_t originY, int64_t originZ, std::span< const reaction::RxCell > cells)
std::vector< std::byte > SerializeLabRecording(const LabRecording &recording)
std::optional< LabMismatch > FindLabMismatch(const MultiresNest &cpu, const MultiresNest &gpu, uint64_t tick)
constexpr int32_t LAB_INITIAL_TEMPERATURE_MILLIKELVIN
void StepNest(MultiresNest &nest, const BakedReactionTable &table, uint64_t worldSeed, uint64_t tick, const MultiresStepOptions &options)
lab::LabCommand ToLabCommand(const Command &command)
MultiresCapacity LabBoxCapacity(const BakedReactionTable &table)
uint32_t SpeciesId(std::string_view name) const
std::vector< reaction::RxSpecies > species
ReactionTableView View() const
std::array< uint32_t, COMMAND_PAYLOAD_WORDS > payload
std::vector< uint64_t > hashes
std::vector< Command > commands
std::vector< multires::MrBlock > blocks
std::vector< multires::MrFraction > fractions
std::vector< uint32_t > freeFractions
std::vector< uint64_t > ledger
std::vector< uint32_t > freeBlocks
std::array< uint32_t, multires::MR_COUNTER_COUNT > counters