diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp index 5d2cf55a..f142f34c 100644 --- a/src/blocks/looksblocks.cpp +++ b/src/blocks/looksblocks.cpp @@ -964,8 +964,9 @@ void LooksBlocks::randomBackdropImpl(VirtualMachine *vm) if (Stage *stage = vm->engine()->stage()) { std::size_t count = stage->costumes().size(); - if (count > 0) - stage->setCostumeIndex(rng->randint(0, count - 1)); + if (count > 1) { + stage->setCostumeIndex(rng->randintExcept(0, count - 1, stage->costumeIndex())); + } } } diff --git a/src/engine/internal/irandomgenerator.h b/src/engine/internal/irandomgenerator.h index c8d4a177..2228c933 100644 --- a/src/engine/internal/irandomgenerator.h +++ b/src/engine/internal/irandomgenerator.h @@ -12,6 +12,7 @@ class IRandomGenerator virtual long randint(long start, long end) const = 0; virtual double randintDouble(double start, double end) const = 0; + virtual long randintExcept(long start, long end, long except) const = 0; }; } // namespace libscratchcpp diff --git a/src/engine/internal/randomgenerator.cpp b/src/engine/internal/randomgenerator.cpp index 06e169e7..e9e7c572 100644 --- a/src/engine/internal/randomgenerator.cpp +++ b/src/engine/internal/randomgenerator.cpp @@ -41,3 +41,29 @@ double RandomGenerator::randintDouble(double start, double end) const std::uniform_real_distribution distribution(start, end); return distribution(*m_generator); } + +long RandomGenerator::randintExcept(long start, long end, long except) const +{ + if (start > end) { + std::swap(start, end); + } + + if (except < start || except > end) { + return randint(start, end); + } else if (start == end) { + return start; + } else if (end - start == 1) { + if (except == start) + return end; + else + return start; + } + + if (randint(0, 1) == 0) { + std::uniform_int_distribution distribution(start, except - 1); + return distribution(*m_generator); + } else { + std::uniform_int_distribution distribution(except + 1, end); + return distribution(*m_generator); + } +} diff --git a/src/engine/internal/randomgenerator.h b/src/engine/internal/randomgenerator.h index 6cb72586..1358de37 100644 --- a/src/engine/internal/randomgenerator.h +++ b/src/engine/internal/randomgenerator.h @@ -20,6 +20,7 @@ class RandomGenerator : public IRandomGenerator long randint(long start, long end) const override; double randintDouble(double start, double end) const override; + long randintExcept(long start, long end, long except) const; private: static std::shared_ptr m_instance; diff --git a/test/blocks/looks_blocks_test.cpp b/test/blocks/looks_blocks_test.cpp index 92c21532..041c32b8 100644 --- a/test/blocks/looks_blocks_test.cpp +++ b/test/blocks/looks_blocks_test.cpp @@ -1992,7 +1992,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToImpl) LooksBlocks::rng = &rng; EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(3)->broadcast(), &thread, false)); - EXPECT_CALL(rng, randint(0, 5)).WillOnce(Return(3)); + EXPECT_CALL(rng, randintExcept(0, 5, 0)).WillOnce(Return(3)); stage.setCostumeIndex(0); vm->setBytecode(bytecode15); vm->run(); @@ -2001,7 +2001,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToImpl) ASSERT_EQ(stage.costumeIndex(), 3); EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(5)->broadcast(), &thread, false)); - EXPECT_CALL(rng, randint(0, 5)).WillOnce(Return(5)); + EXPECT_CALL(rng, randintExcept(0, 5, 3)).WillOnce(Return(5)); vm->reset(); vm->run(); @@ -2011,7 +2011,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToImpl) stage.addCostume(std::make_shared("random backdrop", "b7", "svg")); EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(6)->broadcast(), &thread, false)); - EXPECT_CALL(rng, randint).Times(0); + EXPECT_CALL(rng, randintExcept).Times(0); vm->reset(); vm->run(); @@ -2337,7 +2337,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToAndWaitImpl) LooksBlocks::rng = &rng; EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(3)->broadcast(), &thread, true)); - EXPECT_CALL(rng, randint(0, 5)).WillOnce(Return(3)); + EXPECT_CALL(rng, randintExcept(0, 5, 0)).WillOnce(Return(3)); stage.setCostumeIndex(0); vm->resolvePromise(); vm->setBytecode(bytecode15); @@ -2349,7 +2349,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToAndWaitImpl) ASSERT_EQ(stage.costumeIndex(), 3); EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(5)->broadcast(), &thread, true)); - EXPECT_CALL(rng, randint(0, 5)).WillOnce(Return(5)); + EXPECT_CALL(rng, randintExcept(0, 5, 3)).WillOnce(Return(5)); vm->reset(); vm->run(); vm->resolvePromise(); @@ -2361,7 +2361,7 @@ TEST_F(LooksBlocksTest, SwitchBackdropToAndWaitImpl) stage.addCostume(std::make_shared("random backdrop", "b7", "svg")); EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(6)->broadcast(), &thread, true)); - EXPECT_CALL(rng, randint).Times(0); + EXPECT_CALL(rng, randintExcept).Times(0); vm->reset(); vm->run(); vm->resolvePromise(); @@ -2488,7 +2488,7 @@ TEST_F(LooksBlocksTest, RandomBackdrop) RandomGeneratorMock rng; LooksBlocks::rng = &rng; - EXPECT_CALL(rng, randint).Times(0); + EXPECT_CALL(rng, randintExcept).Times(0); vm->run(); ASSERT_EQ(vm->registerCount(), 0); @@ -2496,8 +2496,9 @@ TEST_F(LooksBlocksTest, RandomBackdrop) stage.addCostume(std::make_shared("backdrop1", "b1", "svg")); stage.addCostume(std::make_shared("backdrop2", "b2", "svg")); stage.addCostume(std::make_shared("backdrop3", "b3", "svg")); + stage.setCostumeIndex(0); - EXPECT_CALL(rng, randint(0, 2)).WillOnce(Return(1)); + EXPECT_CALL(rng, randintExcept(0, 2, 0)).WillOnce(Return(1)); EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(1)->broadcast(), &thread, false)); vm->reset(); vm->run(); @@ -2507,7 +2508,7 @@ TEST_F(LooksBlocksTest, RandomBackdrop) stage.addCostume(std::make_shared("backdrop4", "b4", "svg")); - EXPECT_CALL(rng, randint(0, 3)).WillOnce(Return(2)); + EXPECT_CALL(rng, randintExcept(0, 3, 1)).WillOnce(Return(2)); EXPECT_CALL(m_engineMock, startBackdropScripts(stage.costumeAt(2)->broadcast(), &thread, false)); vm->reset(); vm->run(); diff --git a/test/mocks/randomgeneratormock.h b/test/mocks/randomgeneratormock.h index 42d6e999..6e07f881 100644 --- a/test/mocks/randomgeneratormock.h +++ b/test/mocks/randomgeneratormock.h @@ -10,4 +10,5 @@ class RandomGeneratorMock : public IRandomGenerator public: MOCK_METHOD(long, randint, (long, long), (const, override)); MOCK_METHOD(double, randintDouble, (double, double), (const, override)); + MOCK_METHOD(long, randintExcept, (long, long, long), (const, override)); }; diff --git a/test/randomgenerator/randomgenerator_test.cpp b/test/randomgenerator/randomgenerator_test.cpp index 45f220af..06b9dda0 100644 --- a/test/randomgenerator/randomgenerator_test.cpp +++ b/test/randomgenerator/randomgenerator_test.cpp @@ -43,3 +43,57 @@ TEST(RandomGeneratorTest, RandIntDouble) ASSERT_LE(num, 5.081); } } + +TEST(RandomGeneratorTest, RandIntExcept) +{ + auto rng = RandomGenerator::instance(); + ASSERT_TRUE(rng); + + long num; + + for (int i = 0; i < 25; i++) { + num = rng->randintExcept(-2, 3, 0); + ASSERT_GE(num, -2); + ASSERT_LE(num, 3); + ASSERT_NE(num, 0); + } + + for (int i = 0; i < 25; i++) { + num = rng->randintExcept(5, -3, 2); + ASSERT_GE(num, -3); + ASSERT_LE(num, 5); + ASSERT_NE(num, 2); + } + + for (int i = 0; i < 25; i++) { + num = rng->randintExcept(5, 8, 2); + ASSERT_GE(num, 5); + ASSERT_LE(num, 8); + } + + for (int i = 0; i < 25; i++) { + num = rng->randintExcept(5, 8, 10); + ASSERT_GE(num, 5); + ASSERT_LE(num, 8); + } + + for (int i = 0; i < 25; i++) { + num = rng->randintExcept(2, 2, 2); + ASSERT_EQ(num, 2); + } + + for (int i = 0; i < 25; i++) { + num = rng->randintExcept(2, 2, 5); + ASSERT_EQ(num, 2); + } + + for (int i = 0; i < 25; i++) { + num = rng->randintExcept(1, 2, 1); + ASSERT_EQ(num, 2); + } + + for (int i = 0; i < 25; i++) { + num = rng->randintExcept(1, 2, 2); + ASSERT_EQ(num, 1); + } +}