260 lines
8.5 KiB
C++
260 lines
8.5 KiB
C++
#include "testHarness.h"
|
|
#include "vf/Circuit.h"
|
|
|
|
using namespace vf;
|
|
|
|
namespace {
|
|
|
|
void wireLine(World& world, int x0, int x1, int y, int z) {
|
|
for (int x = x0; x <= x1; ++x) world.setBlock(x, y, z, Block::RedstoneWire);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
VF_TEST(wirePropagatesWithFalloffToZeroAfterFifteenBlocks) {
|
|
World world;
|
|
world.setBlock(0, 0, 0, Block::RedstoneBlock);
|
|
wireLine(world, 1, 16, 0, 0);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {17, 1, 1});
|
|
circuit.tick();
|
|
|
|
for (int x = 1; x <= 15; ++x) CHECK_EQ(circuit.powerAt(x, 0, 0), redstoneMaxPower - (x - 1));
|
|
CHECK_EQ(circuit.powerAt(16, 0, 0), 0);
|
|
}
|
|
|
|
VF_TEST(wireDoesNotCrossThroughSolidBlocks) {
|
|
World world;
|
|
world.setBlock(0, 0, 0, Block::RedstoneBlock);
|
|
wireLine(world, 1, 3, 0, 0);
|
|
world.setBlock(4, 0, 0, Block::Stone);
|
|
wireLine(world, 5, 7, 0, 0);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {8, 1, 1});
|
|
circuit.tick();
|
|
|
|
CHECK(circuit.powerAt(3, 0, 0) > 0);
|
|
CHECK_EQ(circuit.powerAt(5, 0, 0), 0);
|
|
CHECK_EQ(circuit.powerAt(6, 0, 0), 0);
|
|
}
|
|
|
|
VF_TEST(leverTogglesWireOnAndOff) {
|
|
World world;
|
|
world.setBlock(5, 0, 5, Block::Lever);
|
|
world.setBlock(6, 0, 5, Block::RedstoneWire);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {10, 3, 10});
|
|
|
|
circuit.tick();
|
|
CHECK_EQ(circuit.powerAt(6, 0, 5), 0);
|
|
CHECK(!circuit.leverOn({5, 0, 5}));
|
|
|
|
circuit.setLever({5, 0, 5}, true);
|
|
circuit.tick();
|
|
CHECK(circuit.leverOn({5, 0, 5}));
|
|
CHECK_EQ(circuit.powerAt(6, 0, 5), redstoneMaxPower);
|
|
|
|
circuit.setLever({5, 0, 5}, false);
|
|
circuit.tick();
|
|
CHECK_EQ(circuit.powerAt(6, 0, 5), 0);
|
|
}
|
|
|
|
VF_TEST(buttonPowersThenExpires) {
|
|
World world;
|
|
world.setBlock(5, 0, 5, Block::Button);
|
|
world.setBlock(6, 0, 5, Block::RedstoneWire);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {10, 3, 10});
|
|
circuit.pressButton({5, 0, 5}, 3);
|
|
|
|
circuit.tick();
|
|
CHECK(circuit.buttonActive({5, 0, 5}));
|
|
CHECK_EQ(circuit.powerAt(6, 0, 5), redstoneMaxPower);
|
|
|
|
circuit.tick();
|
|
CHECK(circuit.buttonActive({5, 0, 5}));
|
|
CHECK_EQ(circuit.powerAt(6, 0, 5), redstoneMaxPower);
|
|
|
|
circuit.tick();
|
|
CHECK(!circuit.buttonActive({5, 0, 5}));
|
|
CHECK_EQ(circuit.powerAt(6, 0, 5), 0);
|
|
|
|
circuit.tick();
|
|
CHECK(!circuit.buttonActive({5, 0, 5}));
|
|
CHECK_EQ(circuit.powerAt(6, 0, 5), 0);
|
|
}
|
|
|
|
VF_TEST(torchInvertsThePowerStateOfItsMountBlock) {
|
|
World world;
|
|
world.setBlock(4, 0, 5, Block::RedstoneBlock);
|
|
world.setBlock(5, 0, 5, Block::Stone);
|
|
world.setBlock(5, 1, 5, Block::RedstoneTorch);
|
|
world.setBlock(6, 1, 5, Block::RedstoneWire);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {10, 3, 10});
|
|
|
|
circuit.tick();
|
|
CHECK_EQ(circuit.powerAt(6, 1, 5), redstoneMaxPower); // mount not yet observed as powered
|
|
|
|
circuit.tick();
|
|
CHECK_EQ(circuit.powerAt(6, 1, 5), 0); // mount observed powered, torch inverted off
|
|
}
|
|
|
|
VF_TEST(torchWithUnpoweredMountStaysLitIndefinitely) {
|
|
World world;
|
|
world.setBlock(5, 0, 5, Block::Stone);
|
|
world.setBlock(5, 1, 5, Block::RedstoneTorch);
|
|
world.setBlock(6, 1, 5, Block::RedstoneWire);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {10, 3, 10});
|
|
|
|
for (int i = 0; i < 5; ++i) {
|
|
circuit.tick();
|
|
CHECK_EQ(circuit.powerAt(6, 1, 5), redstoneMaxPower);
|
|
}
|
|
}
|
|
|
|
VF_TEST(repeaterDelaysOutputByConfiguredTicksAndRefreshesToFull) {
|
|
World world;
|
|
world.setBlock(5, 0, 5, Block::Lever);
|
|
world.setBlock(6, 0, 5, Block::Repeater);
|
|
wireLine(world, 7, 20, 0, 5);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {21, 1, 10});
|
|
circuit.setRepeater({6, 0, 5}, Face::PosX, 4);
|
|
circuit.setLever({5, 0, 5}, true);
|
|
|
|
circuit.tick(); // edge not visible yet (fresh field)
|
|
CHECK(!circuit.repeaterOutputOn({6, 0, 5}));
|
|
circuit.tick(); // edge observed, countdown starts at 4 and ticks to 3
|
|
CHECK(!circuit.repeaterOutputOn({6, 0, 5}));
|
|
circuit.tick();
|
|
CHECK(!circuit.repeaterOutputOn({6, 0, 5}));
|
|
circuit.tick();
|
|
CHECK(!circuit.repeaterOutputOn({6, 0, 5}));
|
|
circuit.tick();
|
|
CHECK(circuit.repeaterOutputOn({6, 0, 5}));
|
|
CHECK_EQ(circuit.powerAt(7, 0, 5), redstoneMaxPower); // output refreshed to full strength
|
|
}
|
|
|
|
VF_TEST(repeaterWithMinimumDelayRespondsOneTickAfterTheEdge) {
|
|
World world;
|
|
world.setBlock(5, 0, 5, Block::Lever);
|
|
world.setBlock(6, 0, 5, Block::Repeater);
|
|
world.setBlock(7, 0, 5, Block::RedstoneWire);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {10, 3, 10});
|
|
circuit.setRepeater({6, 0, 5}, Face::PosX, 1);
|
|
circuit.setLever({5, 0, 5}, true);
|
|
|
|
circuit.tick();
|
|
CHECK(!circuit.repeaterOutputOn({6, 0, 5}));
|
|
circuit.tick();
|
|
CHECK(circuit.repeaterOutputOn({6, 0, 5}));
|
|
}
|
|
|
|
VF_TEST(repeaterIgnoresInputPerpendicularToItsFacing) {
|
|
World world;
|
|
world.setBlock(5, 0, 6, Block::Lever); // powers from the side, not behind
|
|
world.setBlock(5, 0, 5, Block::Repeater);
|
|
world.setBlock(6, 0, 5, Block::RedstoneWire);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {10, 3, 10});
|
|
circuit.setRepeater({5, 0, 5}, Face::PosX, 1);
|
|
circuit.setLever({5, 0, 6}, true);
|
|
|
|
for (int i = 0; i < 4; ++i) circuit.tick();
|
|
CHECK(!circuit.repeaterOutputOn({5, 0, 5}));
|
|
CHECK_EQ(circuit.powerAt(6, 0, 5), 0);
|
|
}
|
|
|
|
VF_TEST(comparatorCompareModePassesBackWhenSideIsLower) {
|
|
World world;
|
|
world.setBlock(5, 0, 5, Block::RedstoneBlock);
|
|
world.setBlock(6, 0, 5, Block::Comparator);
|
|
world.setBlock(7, 0, 5, Block::RedstoneWire);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {10, 3, 10});
|
|
circuit.setComparator({6, 0, 5}, Face::PosX, ComparatorMode::Compare);
|
|
|
|
circuit.tick();
|
|
circuit.tick();
|
|
CHECK_EQ(circuit.powerAt(7, 0, 5), redstoneMaxPower);
|
|
}
|
|
|
|
VF_TEST(comparatorCompareModeBlocksWhenSideExceedsBack) {
|
|
World world;
|
|
world.setBlock(3, 0, 5, Block::RedstoneBlock);
|
|
wireLine(world, 4, 5, 0, 5); // back input falls off to 14 by the comparator
|
|
world.setBlock(6, 0, 5, Block::Comparator);
|
|
world.setBlock(7, 0, 5, Block::RedstoneWire);
|
|
world.setBlock(6, 0, 6, Block::RedstoneBlock); // full-strength side input
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {10, 3, 10});
|
|
circuit.setComparator({6, 0, 5}, Face::PosX, ComparatorMode::Compare);
|
|
|
|
circuit.tick();
|
|
circuit.tick();
|
|
CHECK_EQ(circuit.powerAt(7, 0, 5), 0);
|
|
}
|
|
|
|
VF_TEST(comparatorSubtractModeSubtractsSideFromBack) {
|
|
World world;
|
|
world.setBlock(4, 0, 5, Block::RedstoneBlock);
|
|
wireLine(world, 5, 5, 0, 5);
|
|
world.setBlock(6, 0, 5, Block::Comparator);
|
|
world.setBlock(7, 0, 5, Block::RedstoneWire);
|
|
world.setBlock(6, 0, 6, Block::Lever);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {10, 3, 10});
|
|
circuit.setComparator({6, 0, 5}, Face::PosX, ComparatorMode::Subtract);
|
|
circuit.setLever({6, 0, 6}, true);
|
|
|
|
circuit.tick();
|
|
circuit.tick();
|
|
CHECK_EQ(circuit.powerAt(7, 0, 5), 0); // full back (15) minus full side (15) = 0
|
|
}
|
|
|
|
VF_TEST(redstoneBlockIsAConstantSourceRegardlessOfTicks) {
|
|
World world;
|
|
world.setBlock(5, 0, 5, Block::RedstoneBlock);
|
|
wireLine(world, 6, 8, 0, 5);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {9, 1, 10});
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
circuit.tick();
|
|
CHECK_EQ(circuit.powerAt(6, 0, 5), redstoneMaxPower);
|
|
CHECK_EQ(circuit.powerAt(7, 0, 5), redstoneMaxPower - 1);
|
|
}
|
|
}
|
|
|
|
VF_TEST(lampLitsWhenAdjacentToPowerAndDarkensWhenNot) {
|
|
World world;
|
|
world.setBlock(5, 0, 5, Block::Lever);
|
|
world.setBlock(6, 0, 5, Block::RedstoneLamp);
|
|
RedstoneCircuit circuit(world, {0, 0, 0}, {10, 3, 10});
|
|
|
|
circuit.tick();
|
|
circuit.applyLamps();
|
|
CHECK(world.blockAt(6, 0, 5) == Block::RedstoneLamp);
|
|
|
|
circuit.setLever({5, 0, 5}, true);
|
|
circuit.tick();
|
|
circuit.applyLamps();
|
|
CHECK(world.blockAt(6, 0, 5) == Block::RedstoneLampLit);
|
|
|
|
circuit.setLever({5, 0, 5}, false);
|
|
circuit.tick();
|
|
circuit.applyLamps();
|
|
CHECK(world.blockAt(6, 0, 5) == Block::RedstoneLamp);
|
|
}
|
|
|
|
VF_TEST(circuitIsDeterministicAcrossIdenticalRuns) {
|
|
World worldA, worldB;
|
|
for (World* w : {&worldA, &worldB}) {
|
|
w->setBlock(4, 0, 5, Block::RedstoneBlock);
|
|
w->setBlock(5, 0, 5, Block::Stone);
|
|
w->setBlock(5, 1, 5, Block::RedstoneTorch);
|
|
wireLine(*w, 6, 12, 1, 5);
|
|
w->setBlock(6, 1, 6, Block::Lever);
|
|
}
|
|
RedstoneCircuit a(worldA, {0, 0, 0}, {14, 3, 10});
|
|
RedstoneCircuit b(worldB, {0, 0, 0}, {14, 3, 10});
|
|
a.setLever({6, 1, 6}, true);
|
|
b.setLever({6, 1, 6}, true);
|
|
|
|
for (int i = 0; i < 6; ++i) {
|
|
a.tick();
|
|
b.tick();
|
|
for (int x = 6; x <= 12; ++x) CHECK_EQ(a.powerAt(x, 1, 5), b.powerAt(x, 1, 5));
|
|
}
|
|
}
|