From 52ee433e7f9ce63b392bc51d90629f4374c33c72 Mon Sep 17 00:00:00 2001 From: nathyong Date: Thu, 3 Dec 2020 11:51:20 +1100 Subject: [PATCH 1/2] Add the Tetra Legends Tetra-X randomizer This randomizer is a slightly cleaned up version of Dr Ocelot's JavaScript variant[1], including the 1000-piece selection mechanism. [1]: https://github.com/Dr-Ocelot/tetralegends/blob/8d57a703224cc5aa5260e13160e973755c7551d9/script/game/modules/randomizers.js --- tetris/randomizers/tetra.lua | 71 ++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tetris/randomizers/tetra.lua diff --git a/tetris/randomizers/tetra.lua b/tetris/randomizers/tetra.lua new file mode 100644 index 0000000..4b3f682 --- /dev/null +++ b/tetris/randomizers/tetra.lua @@ -0,0 +1,71 @@ +-- A randomizer based on Tetra Legends' "Tetra-X" game mode +-- There are two main rules: +-- * if piece hasn't been generated for 13+ pieces, force it +-- * if piece has been generated in the last two pieces, don't give it out +-- +-- The official implementation also has self-balancing functionality (bias +-- towards pieces that have appeared less often). + +local Randomizer = require 'tetris.randomizers.randomizer' + +local TetraXRandomizer = Randomizer:extend() + +function TetraXRandomizer:initialize() + local pieces = {"I", "J", "L", "O", "S", "T", "Z"} + + self.count = {} + self.lastseen = {} + self.totalcount = 0 + self.pieceselection = pieces + + for _, piece in ipairs(pieces) do + self.count[piece] = 0 + self.lastseen[piece] = -1 -- use -1 as magic value for "not seen" + end + +end + +function TetraXRandomizer:generatePiece() + local generated = nil + while true do + generated = self.pieceselection[math.random(#self.pieceselection)] + if not (self.lastseen[generated] == 0 or self.lastseen[generated] == 1) then + break + end + end + + -- piece has not been generated in the last 13+ + for piece, val in pairs(self.lastseen) do + if val >= 13 then + generated = piece + break + end + end + + for piece, val in pairs(self.lastseen) do + if piece == generated then + self.lastseen[piece] = 0 + else + if val ~= -1 then + self.lastseen[piece] = val + 1 + end + end + end + + self.count[generated] = self.count[generated] + 1 + self.totalcount = self.totalcount + 1 + + -- shuffle the piece selection for the next time + self.pieceselection = {} + for piece, count in pairs(self.count) do + local probability = (self.totalcount - count) / (self.totalcount * 6) + local chances = math.floor(probability * 1000 + 0.5) -- simulated "round" + for _ = 1, chances do + table.insert(self.pieceselection, piece) + end + end + + return generated +end + +return TetraXRandomizer From a4c650922272b38e9f5504e85119f1a2a577698c Mon Sep 17 00:00:00 2001 From: nathyong Date: Thu, 3 Dec 2020 12:56:02 +1100 Subject: [PATCH 2/2] Document magic value in Tetra-X randomizer --- tetris/randomizers/tetra.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/tetris/randomizers/tetra.lua b/tetris/randomizers/tetra.lua index 4b3f682..7dcea7e 100644 --- a/tetris/randomizers/tetra.lua +++ b/tetris/randomizers/tetra.lua @@ -58,6 +58,7 @@ function TetraXRandomizer:generatePiece() -- shuffle the piece selection for the next time self.pieceselection = {} for piece, count in pairs(self.count) do + -- in this case 6 = #piece types - 1, so all probabilities sum to 1 local probability = (self.totalcount - count) / (self.totalcount * 6) local chances = math.floor(probability * 1000 + 0.5) -- simulated "round" for _ = 1, chances do