mirror of
https://github.com/SashLilac/cambridge.git
synced 2025-05-13 20:21:25 -05:00
Compare commits
37 Commits
v0.3-pre1
...
highscore_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f570306f5 | ||
|
|
b22f671409 | ||
|
|
0b6f62d50e | ||
|
|
086f327371 | ||
|
|
3c83ae0bf4 | ||
|
|
450833b246 | ||
|
|
8e7a5418dc | ||
|
|
6609b642dc | ||
|
|
452879ebab | ||
|
|
70a827b477 | ||
|
|
d281a732db | ||
|
|
01e91fbd93 | ||
|
|
ece853c9d3 | ||
|
|
ea8d008370 | ||
|
|
e20eb048c8 | ||
|
|
a33ca1af24 | ||
|
|
664bca2282 | ||
|
|
fc8fb8b66f | ||
|
|
fc58e6e908 | ||
|
|
061f6f5164 | ||
|
|
4e9cea7dda | ||
|
|
fa97216167 | ||
|
|
3f8d68cc9d | ||
|
|
6639d73c1c | ||
|
|
668f061077 | ||
|
|
cb70967b82 | ||
|
|
0c2ba5f0cc | ||
|
|
6d07a3b820 | ||
|
|
2de13a97f0 | ||
|
|
512c2149f0 | ||
|
|
6fb19220b7 | ||
|
|
08da67c434 | ||
|
|
2d63ca8ee1 | ||
|
|
0f09d47e60 | ||
|
|
9d44d1e771 | ||
|
|
5d022f9037 | ||
|
|
818743fe77 |
32
SOURCES.md
32
SOURCES.md
@@ -139,4 +139,34 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
bigint.lua (https://github.com/empyreuma/bigint.lua)
|
||||
--------------------
|
||||
|
||||
3-Clause BSD License
|
||||
|
||||
Copyright (c) Emily "empyreuma" 2016
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
27
funcs.lua
27
funcs.lua
@@ -56,9 +56,19 @@ end
|
||||
|
||||
function formatBigNum(number)
|
||||
-- returns a string representing a number with commas as thousands separator (e.g. 12,345,678)
|
||||
local s = string.format("%d", number)
|
||||
local pos = string.len(s) % 3
|
||||
if pos == 0 then pos = 3 end
|
||||
local s
|
||||
if type(number) == "number" then
|
||||
s = string.format("%d", number)
|
||||
elseif type(number) == "string" then
|
||||
if not tonumber(number) then
|
||||
return
|
||||
else
|
||||
s = number
|
||||
end
|
||||
else
|
||||
return
|
||||
end
|
||||
local pos = Mod1(string.len(s), 3)
|
||||
return string.sub(s, 1, pos)
|
||||
.. string.gsub(string.sub(s, pos+1), "(...)", ",%1")
|
||||
end
|
||||
@@ -66,4 +76,13 @@ end
|
||||
function Mod1(n, m)
|
||||
-- returns a number congruent to n modulo m in the range [1;m] (as opposed to [0;m-1])
|
||||
return ((n-1) % m) + 1
|
||||
end
|
||||
end
|
||||
|
||||
function table.contains(table, element)
|
||||
for _, value in pairs(table) do
|
||||
if value == element then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
558
libs/bigint/bigint.lua
Normal file
558
libs/bigint/bigint.lua
Normal file
@@ -0,0 +1,558 @@
|
||||
#!/usr/bin/env lua
|
||||
-- If this variable is true, then strict type checking is performed for all
|
||||
-- operations. This may result in slower code, but it will allow you to catch
|
||||
-- errors and bugs earlier.
|
||||
local strict = true
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local bigint = {}
|
||||
|
||||
local named_powers = require("libs.bigint.named-powers-of-ten")
|
||||
|
||||
-- Create a new bigint or convert a number or string into a big
|
||||
-- Returns an empty, positive bigint if no number or string is given
|
||||
function bigint.new(num)
|
||||
local self = {
|
||||
sign = "+",
|
||||
digits = {}
|
||||
}
|
||||
|
||||
-- Return a new bigint with the same sign and digits
|
||||
function self:clone()
|
||||
local newint = bigint.new()
|
||||
newint.sign = self.sign
|
||||
for _, digit in pairs(self.digits) do
|
||||
newint.digits[#newint.digits + 1] = digit
|
||||
end
|
||||
return newint
|
||||
end
|
||||
|
||||
setmetatable(self, {
|
||||
__add = function(lhs, rhs)
|
||||
return bigint.add(lhs, rhs)
|
||||
end,
|
||||
__unm = function()
|
||||
if (self.sign == "+") then
|
||||
self.sign = "-"
|
||||
else
|
||||
self.sign = "+"
|
||||
end
|
||||
return self
|
||||
end,
|
||||
__sub = function(lhs, rhs)
|
||||
return bigint.subtract(lhs, rhs)
|
||||
end,
|
||||
__mul = function(lhs, rhs)
|
||||
return bigint.multiply(lhs, rhs)
|
||||
end,
|
||||
__div = function(lhs, rhs)
|
||||
return bigint.divide(lhs, rhs)
|
||||
end,
|
||||
__mod = function(lhs, rhs)
|
||||
return bigint.modulus(lhs, rhs)
|
||||
end,
|
||||
__pow = function(lhs, rhs)
|
||||
return bigint.exponentiate(lhs, rhs)
|
||||
end,
|
||||
__eq = function(lhs, rhs)
|
||||
return bigint.compare(lhs, rhs, "==")
|
||||
end,
|
||||
__lt = function(lhs, rhs)
|
||||
return bigint.compare(lhs, rhs, "<")
|
||||
end,
|
||||
__le = function(lhs, rhs)
|
||||
return bigint.compare(lhs, rhs, "<=")
|
||||
end,
|
||||
__tostring = function()
|
||||
return bigint.unserialize(self, "s")
|
||||
end
|
||||
})
|
||||
|
||||
if (num) then
|
||||
local num_string = tostring(num)
|
||||
for digit in string.gmatch(num_string, "[0-9]") do
|
||||
table.insert(self.digits, tonumber(digit))
|
||||
end
|
||||
if string.sub(num_string, 1, 1) == "-" then
|
||||
self.sign = "-"
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-- Check the type of a big
|
||||
-- Normally only runs when global variable "strict" == true, but checking can be
|
||||
-- forced by supplying "true" as the second argument.
|
||||
function bigint.check(big, force)
|
||||
if (strict or force) then
|
||||
assert(#big.digits > 0, "bigint is empty")
|
||||
assert(type(big.sign) == "string", "bigint is unsigned")
|
||||
for _, digit in pairs(big.digits) do
|
||||
assert(type(digit) == "number", digit .. " is not a number")
|
||||
assert(digit < 10, digit .. " is greater than or equal to 10")
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Return a new big with the same digits but with a positive sign (absolute
|
||||
-- value)
|
||||
function bigint.abs(big)
|
||||
bigint.check(big)
|
||||
local result = big:clone()
|
||||
result.sign = "+"
|
||||
return result
|
||||
end
|
||||
|
||||
-- Convert a big to a number or string
|
||||
function bigint.unserialize(big, output_type, precision)
|
||||
bigint.check(big)
|
||||
|
||||
local num = ""
|
||||
if big.sign == "-" then
|
||||
num = "-"
|
||||
end
|
||||
|
||||
|
||||
if ((output_type == nil)
|
||||
or (output_type == "number")
|
||||
or (output_type == "n")
|
||||
or (output_type == "string")
|
||||
or (output_type == "s")) then
|
||||
-- Unserialization to a string or number requires reconstructing the
|
||||
-- entire number
|
||||
|
||||
for _, digit in pairs(big.digits) do
|
||||
num = num .. math.floor(digit) -- lazy way of getting rid of .0$
|
||||
end
|
||||
|
||||
if ((output_type == nil)
|
||||
or (output_type == "number")
|
||||
or (output_type == "n")) then
|
||||
return tonumber(num)
|
||||
else
|
||||
return num
|
||||
end
|
||||
|
||||
else
|
||||
-- Unserialization to human-readable form or scientific notation only
|
||||
-- requires reading the first few digits
|
||||
if (precision == nil) then
|
||||
precision = 3
|
||||
else
|
||||
assert(precision > 0, "Precision cannot be less than 1")
|
||||
assert(math.floor(precision) == precision,
|
||||
"Precision must be a positive integer")
|
||||
end
|
||||
|
||||
-- num is the first (precision + 1) digits, the first being separated by
|
||||
-- a decimal point from the others
|
||||
num = num .. big.digits[1]
|
||||
if (precision > 1) then
|
||||
num = num .. "."
|
||||
for i = 1, (precision - 1) do
|
||||
num = num .. big.digits[i + 1]
|
||||
end
|
||||
end
|
||||
|
||||
if ((output_type == "human-readable")
|
||||
or (output_type == "human")
|
||||
or (output_type == "h")) then
|
||||
-- Human-readable output contributed by 123eee555
|
||||
|
||||
local name
|
||||
local walkback = 0 -- Used to enumerate "ten", "hundred", etc
|
||||
|
||||
-- Walk backwards in the index of named_powers starting at the
|
||||
-- number of digits of the input until the first value is found
|
||||
for i = (#big.digits - 1), (#big.digits - 4), -1 do
|
||||
name = named_powers[i]
|
||||
if (name) then
|
||||
if (walkback == 1) then
|
||||
name = "ten " .. name
|
||||
elseif (walkback == 2) then
|
||||
name = "hundred " .. name
|
||||
end
|
||||
break
|
||||
else
|
||||
walkback = walkback + 1
|
||||
end
|
||||
end
|
||||
|
||||
return num .. " " .. name
|
||||
|
||||
else
|
||||
return num .. "*10^" .. (#big.digits - 1)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Basic comparisons
|
||||
-- Accepts symbols (<, >=, ~=) and Unix shell-like options (lt, ge, ne)
|
||||
function bigint.compare(big1, big2, comparison)
|
||||
bigint.check(big1)
|
||||
bigint.check(big2)
|
||||
|
||||
local greater = false -- If big1.digits > big2.digits
|
||||
local equal = false
|
||||
|
||||
if (big1.sign == "-") and (big2.sign == "+") then
|
||||
greater = false
|
||||
elseif (#big1.digits > #big2.digits)
|
||||
or ((big1.sign == "+") and (big2.sign == "-")) then
|
||||
greater = true
|
||||
elseif (#big1.digits == #big2.digits) then
|
||||
-- Walk left to right, comparing digits
|
||||
for digit = 1, #big1.digits do
|
||||
if (big1.digits[digit] > big2.digits[digit]) then
|
||||
greater = true
|
||||
break
|
||||
elseif (big2.digits[digit] > big1.digits[digit]) then
|
||||
break
|
||||
elseif (digit == #big1.digits)
|
||||
and (big1.digits[digit] == big2.digits[digit]) then
|
||||
equal = true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- If both numbers are negative, then the requirements for greater are
|
||||
-- reversed
|
||||
if (not equal) and (big1.sign == "-") and (big2.sign == "-") then
|
||||
greater = not greater
|
||||
end
|
||||
|
||||
return (((comparison == "<") or (comparison == "lt"))
|
||||
and ((not greater) and (not equal)) and true)
|
||||
or (((comparison == ">") or (comparison == "gt"))
|
||||
and ((greater) and (not equal)) and true)
|
||||
or (((comparison == "==") or (comparison == "eq"))
|
||||
and (equal) and true)
|
||||
or (((comparison == ">=") or (comparison == "ge"))
|
||||
and (equal or greater) and true)
|
||||
or (((comparison == "<=") or (comparison == "le"))
|
||||
and (equal or not greater) and true)
|
||||
or (((comparison == "~=") or (comparison == "!=") or (comparison == "ne"))
|
||||
and (not equal) and true)
|
||||
or false
|
||||
end
|
||||
|
||||
-- BACKEND: Add big1 and big2, ignoring signs
|
||||
function bigint.add_raw(big1, big2)
|
||||
bigint.check(big1)
|
||||
bigint.check(big2)
|
||||
|
||||
local result = bigint.new()
|
||||
local max_digits = 0
|
||||
local carry = 0
|
||||
|
||||
if (#big1.digits >= #big2.digits) then
|
||||
max_digits = #big1.digits
|
||||
else
|
||||
max_digits = #big2.digits
|
||||
end
|
||||
|
||||
-- Walk backwards right to left, like in long addition
|
||||
for digit = 0, max_digits - 1 do
|
||||
local sum = (big1.digits[#big1.digits - digit] or 0)
|
||||
+ (big2.digits[#big2.digits - digit] or 0)
|
||||
+ carry
|
||||
|
||||
if (sum >= 10) then
|
||||
carry = 1
|
||||
sum = sum - 10
|
||||
else
|
||||
carry = 0
|
||||
end
|
||||
|
||||
result.digits[max_digits - digit] = sum
|
||||
end
|
||||
|
||||
-- Leftover carry in cases when #big1.digits == #big2.digits and sum > 10, ex. 7 + 9
|
||||
if (carry == 1) then
|
||||
table.insert(result.digits, 1, 1)
|
||||
end
|
||||
|
||||
return result
|
||||
|
||||
end
|
||||
|
||||
-- BACKEND: Subtract big2 from big1, ignoring signs
|
||||
function bigint.subtract_raw(big1, big2)
|
||||
-- Type checking is done by bigint.compare
|
||||
assert(bigint.compare(bigint.abs(big1), bigint.abs(big2), ">="),
|
||||
"Size of " .. bigint.unserialize(big1, "string") .. " is less than "
|
||||
.. bigint.unserialize(big2, "string"))
|
||||
|
||||
local result = big1:clone()
|
||||
local max_digits = #big1.digits
|
||||
local borrow = 0
|
||||
|
||||
-- Logic mostly copied from bigint.add_raw ---------------------------------
|
||||
-- Walk backwards right to left, like in long subtraction
|
||||
for digit = 0, max_digits - 1 do
|
||||
local diff = (big1.digits[#big1.digits - digit] or 0)
|
||||
- (big2.digits[#big2.digits - digit] or 0)
|
||||
- borrow
|
||||
|
||||
if (diff < 0) then
|
||||
borrow = 1
|
||||
diff = diff + 10
|
||||
else
|
||||
borrow = 0
|
||||
end
|
||||
|
||||
result.digits[max_digits - digit] = diff
|
||||
end
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
|
||||
-- Strip leading zeroes if any, but not if 0 is the only digit
|
||||
while (#result.digits > 1) and (result.digits[1] == 0) do
|
||||
table.remove(result.digits, 1)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
-- FRONTEND: Addition and subtraction operations, accounting for signs
|
||||
function bigint.add(big1, big2)
|
||||
-- Type checking is done by bigint.compare
|
||||
|
||||
local result
|
||||
|
||||
-- If adding numbers of different sign, subtract the smaller sized one from
|
||||
-- the bigger sized one and take the sign of the bigger sized one
|
||||
if (big1.sign ~= big2.sign) then
|
||||
if (bigint.compare(bigint.abs(big1), bigint.abs(big2), ">")) then
|
||||
result = bigint.subtract_raw(big1, big2)
|
||||
result.sign = big1.sign
|
||||
else
|
||||
result = bigint.subtract_raw(big2, big1)
|
||||
result.sign = big2.sign
|
||||
end
|
||||
|
||||
elseif (big1.sign == "+") and (big2.sign == "+") then
|
||||
result = bigint.add_raw(big1, big2)
|
||||
|
||||
elseif (big1.sign == "-") and (big2.sign == "-") then
|
||||
result = bigint.add_raw(big1, big2)
|
||||
result.sign = "-"
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
function bigint.subtract(big1, big2)
|
||||
-- Type checking is done by bigint.compare in bigint.add
|
||||
-- Subtracting is like adding a negative
|
||||
local big2_local = big2:clone()
|
||||
if (big2.sign == "+") then
|
||||
big2_local.sign = "-"
|
||||
else
|
||||
big2_local.sign = "+"
|
||||
end
|
||||
return bigint.add(big1, big2_local)
|
||||
end
|
||||
|
||||
-- BACKEND: Multiply a big by a single digit big, ignoring signs
|
||||
function bigint.multiply_single(big1, big2)
|
||||
bigint.check(big1)
|
||||
bigint.check(big2)
|
||||
assert(#big2.digits == 1, bigint.unserialize(big2, "string")
|
||||
.. " has more than one digit")
|
||||
|
||||
local result = bigint.new()
|
||||
local carry = 0
|
||||
|
||||
-- Logic mostly copied from bigint.add_raw ---------------------------------
|
||||
-- Walk backwards right to left, like in long multiplication
|
||||
for digit = 0, #big1.digits - 1 do
|
||||
local this_digit = big1.digits[#big1.digits - digit]
|
||||
* big2.digits[1]
|
||||
+ carry
|
||||
|
||||
if (this_digit >= 10) then
|
||||
carry = math.floor(this_digit / 10)
|
||||
this_digit = this_digit - (carry * 10)
|
||||
else
|
||||
carry = 0
|
||||
end
|
||||
|
||||
result.digits[#big1.digits - digit] = this_digit
|
||||
end
|
||||
|
||||
-- Leftover carry in cases when big1.digits[1] * big2.digits[1] > 0
|
||||
if (carry > 0) then
|
||||
table.insert(result.digits, 1, carry)
|
||||
end
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
-- FRONTEND: Multiply two bigs, accounting for signs
|
||||
function bigint.multiply(big1, big2)
|
||||
-- Type checking done by bigint.multiply_single
|
||||
|
||||
local result = bigint.new(0)
|
||||
local larger, smaller -- Larger and smaller in terms of digits, not size
|
||||
|
||||
if (bigint.unserialize(big1) == 0) or (bigint.unserialize(big2) == 0) then
|
||||
return result
|
||||
end
|
||||
|
||||
if (#big1.digits >= #big2.digits) then
|
||||
larger = big1
|
||||
smaller = big2
|
||||
else
|
||||
larger = big2
|
||||
smaller = big1
|
||||
end
|
||||
|
||||
-- Walk backwards right to left, like in long multiplication
|
||||
for digit = 0, #smaller.digits - 1 do
|
||||
-- Sorry for going over column 80! There's lots of big names here
|
||||
local this_digit_product = bigint.multiply_single(larger,
|
||||
bigint.new(smaller.digits[#smaller.digits - digit]))
|
||||
|
||||
-- "Placeholding zeroes"
|
||||
if (digit > 0) then
|
||||
for placeholder = 1, digit do
|
||||
table.insert(this_digit_product.digits, 0)
|
||||
end
|
||||
end
|
||||
|
||||
result = bigint.add(result, this_digit_product)
|
||||
end
|
||||
|
||||
if (larger.sign == smaller.sign) then
|
||||
result.sign = "+"
|
||||
else
|
||||
result.sign = "-"
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
-- Raise a big to a positive integer or big power (TODO: negative integer power)
|
||||
function bigint.exponentiate(big, power)
|
||||
-- Type checking for big done by bigint.multiply
|
||||
assert(bigint.compare(power, bigint.new(0), ">="),
|
||||
" negative powers are not supported")
|
||||
local exp = power:clone()
|
||||
|
||||
if (bigint.compare(exp, bigint.new(0), "==")) then
|
||||
return bigint.new(1)
|
||||
elseif (bigint.compare(exp, bigint.new(1), "==")) then
|
||||
return big
|
||||
else
|
||||
local result = big:clone()
|
||||
|
||||
while (bigint.compare(exp, bigint.new(1), ">")) do
|
||||
result = bigint.multiply(result, big)
|
||||
exp = bigint.subtract(exp, bigint.new(1))
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- BACKEND: Divide two bigs (decimals not supported), returning big result and
|
||||
-- big remainder
|
||||
-- WARNING: Only supports positive integers
|
||||
function bigint.divide_raw(big1, big2)
|
||||
-- Type checking done by bigint.compare
|
||||
if (bigint.compare(big1, big2, "==")) then
|
||||
return bigint.new(1), bigint.new(0)
|
||||
elseif (bigint.compare(big1, big2, "<")) then
|
||||
return bigint.new(0), bigint.new(0)
|
||||
else
|
||||
assert(bigint.compare(big2, bigint.new(0), "!="), "error: divide by zero")
|
||||
assert(big1.sign == "+", "error: big1 is not positive")
|
||||
assert(big2.sign == "+", "error: big2 is not positive")
|
||||
|
||||
local result = bigint.new()
|
||||
|
||||
local dividend = bigint.new() -- Dividend of a single operation, not the
|
||||
-- dividend of the overall function
|
||||
local divisor = big2:clone()
|
||||
local factor = 1
|
||||
|
||||
-- Walk left to right among digits in the dividend, like in long
|
||||
-- division
|
||||
for _, digit in pairs(big1.digits) do
|
||||
dividend.digits[#dividend.digits + 1] = digit
|
||||
|
||||
-- The dividend is smaller than the divisor, so a zero is appended
|
||||
-- to the result and the loop ends
|
||||
if (bigint.compare(dividend, divisor, "<")) then
|
||||
if (#result.digits > 0) then -- Don't add leading zeroes
|
||||
result.digits[#result.digits + 1] = 0
|
||||
end
|
||||
else
|
||||
-- Find the maximum number of divisors that fit into the
|
||||
-- dividend
|
||||
factor = 0
|
||||
while (bigint.compare(divisor, dividend, "<=")) do
|
||||
divisor = bigint.add(divisor, big2)
|
||||
factor = factor + 1
|
||||
end
|
||||
|
||||
-- Append the factor to the result
|
||||
if (factor == 10) then
|
||||
-- Fixes a weird bug that introduces a new bug if fixed by
|
||||
-- changing the comparison in the while loop to "<="
|
||||
result.digits[#result.digits] = 1
|
||||
result.digits[#result.digits + 1] = 0
|
||||
else
|
||||
result.digits[#result.digits + 1] = factor
|
||||
end
|
||||
|
||||
-- Subtract the divisor from the dividend to obtain the
|
||||
-- remainder, which is the new dividend for the next loop
|
||||
dividend = bigint.subtract(dividend,
|
||||
bigint.subtract(divisor, big2))
|
||||
|
||||
-- Reset the divisor
|
||||
divisor = big2:clone()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- The remainder of the final loop is returned as the function's
|
||||
-- overall remainder
|
||||
return result, dividend
|
||||
end
|
||||
end
|
||||
|
||||
-- FRONTEND: Divide two bigs (decimals not supported), returning big result and
|
||||
-- big remainder, accounting for signs
|
||||
function bigint.divide(big1, big2)
|
||||
local result, remainder = bigint.divide_raw(bigint.abs(big1),
|
||||
bigint.abs(big2))
|
||||
if (big1.sign == big2.sign) then
|
||||
result.sign = "+"
|
||||
else
|
||||
result.sign = "-"
|
||||
end
|
||||
|
||||
return result, remainder
|
||||
end
|
||||
|
||||
-- FRONTEND: Return only the remainder from bigint.divide
|
||||
function bigint.modulus(big1, big2)
|
||||
local result, remainder = bigint.divide(big1, big2)
|
||||
|
||||
-- Remainder will always have the same sign as the dividend per C standard
|
||||
-- https://en.wikipedia.org/wiki/Modulo_operation#Remainder_calculation_for_the_modulo_operation
|
||||
remainder.sign = big1.sign
|
||||
return remainder
|
||||
end
|
||||
|
||||
return bigint
|
||||
3340
libs/bigint/named-powers-of-ten.lua
Normal file
3340
libs/bigint/named-powers-of-ten.lua
Normal file
File diff suppressed because it is too large
Load Diff
2
load/bigint.lua
Normal file
2
load/bigint.lua
Normal file
@@ -0,0 +1,2 @@
|
||||
bigint = require "libs.bigint.bigint"
|
||||
number_names = require "libs.bigint.named-powers-of-ten"
|
||||
@@ -61,6 +61,11 @@ blocks = {
|
||||
F = love.graphics.newImage("res/img/gem9.png"),
|
||||
A = love.graphics.newImage("res/img/gem9.png"),
|
||||
X = love.graphics.newImage("res/img/gem9.png"),
|
||||
},
|
||||
["square"] = {
|
||||
F = love.graphics.newImage("res/img/squares.png"),
|
||||
Y = love.graphics.newImage("res/img/squareg.png"),
|
||||
X = love.graphics.newImage("res/img/squares.png"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
3
main.lua
3
main.lua
@@ -7,6 +7,7 @@ function love.load()
|
||||
require "load.sounds"
|
||||
require "load.bgm"
|
||||
require "load.save"
|
||||
require "load.bigint"
|
||||
loadSave()
|
||||
require "scene"
|
||||
--config["side_next"] = false
|
||||
@@ -15,8 +16,10 @@ function love.load()
|
||||
|
||||
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
|
||||
|
||||
-- init config
|
||||
if not config.das then config.das = 10 end
|
||||
if not config.arr then config.arr = 2 end
|
||||
if not config.dcd then config.dcd = 0 end
|
||||
if not config.sfx_volume then config.sfx_volume = 0.5 end
|
||||
if not config.bgm_volume then config.bgm_volume = 0.5 end
|
||||
|
||||
|
||||
BIN
res/img/squareg.png
Normal file
BIN
res/img/squareg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 708 B |
BIN
res/img/squares.png
Normal file
BIN
res/img/squares.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 639 B |
@@ -50,9 +50,37 @@ function GameScene:render()
|
||||
)
|
||||
|
||||
-- game frame
|
||||
love.graphics.draw(misc_graphics["frame"], 48, 64)
|
||||
if self.game.grid.width == 10 and self.game.grid.height == 24 then
|
||||
love.graphics.draw(misc_graphics["frame"], 48, 64)
|
||||
end
|
||||
|
||||
love.graphics.setColor(0, 0, 0, 200)
|
||||
love.graphics.rectangle("fill", 64, 80, 160, 320)
|
||||
love.graphics.rectangle(
|
||||
"fill", 64, 80,
|
||||
16 * self.game.grid.width, 16 * (self.game.grid.height - 4)
|
||||
)
|
||||
|
||||
if self.game.grid.width ~= 10 or self.game.grid.height ~= 24 then
|
||||
love.graphics.setColor(174/255, 83/255, 76/255, 1)
|
||||
love.graphics.setLineWidth(8)
|
||||
love.graphics.line(
|
||||
60,76,
|
||||
68+16*self.game.grid.width,76,
|
||||
68+16*self.game.grid.width,84+16*(self.game.grid.height-4),
|
||||
60,84+16*(self.game.grid.height-4),
|
||||
60,76
|
||||
)
|
||||
love.graphics.setColor(203/255, 137/255, 111/255, 1)
|
||||
love.graphics.setLineWidth(4)
|
||||
love.graphics.line(
|
||||
60,76,
|
||||
68+16*self.game.grid.width,76,
|
||||
68+16*self.game.grid.width,84+16*(self.game.grid.height-4),
|
||||
60,84+16*(self.game.grid.height-4),
|
||||
60,76
|
||||
)
|
||||
love.graphics.setLineWidth(1)
|
||||
end
|
||||
|
||||
self.game:drawGrid()
|
||||
self.game:drawPiece()
|
||||
@@ -76,15 +104,22 @@ function GameScene:render()
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
if self.paused then love.graphics.print("PAUSED!", 80, 100) end
|
||||
|
||||
if self.game.completed then
|
||||
self.game:onGameComplete()
|
||||
elseif self.game.game_over then
|
||||
self.game:onGameOver()
|
||||
end
|
||||
end
|
||||
|
||||
function GameScene:onInputPress(e)
|
||||
if self.game.completed and (e.input == "menu_decide" or e.input == "menu_back" or e.input == "retry") then
|
||||
if (self.game.game_over or self.game.completed) and (e.input == "menu_decide" or e.input == "menu_back" or e.input == "retry") then
|
||||
highscore_entry = self.game:getHighscoreData()
|
||||
highscore_hash = self.game.hash .. "-" .. self.ruleset.hash
|
||||
submitHighscore(highscore_hash, highscore_entry)
|
||||
scene = e.input == "retry" and GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs) or ModeSelectScene()
|
||||
elseif e.input == "retry" then
|
||||
switchBGM(nil)
|
||||
scene = GameScene(self.retry_mode, self.retry_ruleset, self.secret_inputs)
|
||||
elseif e.input == "pause" and not (self.game.game_over or self.game.completed) then
|
||||
self.paused = not self.paused
|
||||
@@ -104,8 +139,19 @@ function GameScene:onInputRelease(e)
|
||||
end
|
||||
|
||||
function submitHighscore(hash, data)
|
||||
function isHighscore(score, high)
|
||||
for k, _ in pairs(score) do
|
||||
if not high[k] or score[k] > high[k] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
if not highscores[hash] then highscores[hash] = {} end
|
||||
table.insert(highscores[hash], data)
|
||||
if isHighscore(data, highscores[hash]) then
|
||||
highscores[hash] = data
|
||||
end
|
||||
saveHighscores()
|
||||
end
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ ConfigScene.options = {
|
||||
{"manlock", "Manual Locking", false, {"Per ruleset", "Per gamemode", "Harddrop", "Softdrop"}},
|
||||
{"piece_colour", "Piece Colours", false, {"Per ruleset", "Arika", "TTC"}},
|
||||
{"world_reverse", "A Button Rotation", false, {"Left", "Auto", "Right"}},
|
||||
{"spawn_positions", "Spawn Positions", false, {"In field", "Out of field"}},
|
||||
{"display_gamemode", "Display Gamemode", false, {"On", "Off"}},
|
||||
{"das_last_key", "DAS Switch", false, {"Default", "Instant"}},
|
||||
{"smooth_movement", "Smooth Piece Drop", false, {"On", "Off"}},
|
||||
@@ -32,8 +33,8 @@ function ConfigScene:new()
|
||||
state = "Changing game settings",
|
||||
})
|
||||
|
||||
self.sfxSlider = newSlider(165, 375, 225, config.sfx_volume * 100, 0, 100, function(v) config.sfx_volume = v / 100 end, {width=20})
|
||||
self.bgmSlider = newSlider(465, 375, 225, config.bgm_volume * 100, 0, 100, function(v) config.bgm_volume = v / 100 end, {width=20})
|
||||
self.sfxSlider = newSlider(165, 400, 225, config.sfx_volume * 100, 0, 100, function(v) config.sfx_volume = v / 100 end, {width=20, knob="circle", track="roundrect"})
|
||||
self.bgmSlider = newSlider(465, 400, 225, config.bgm_volume * 100, 0, 100, function(v) config.bgm_volume = v / 100 end, {width=20, knob="circle", track="roundrect"})
|
||||
end
|
||||
|
||||
function ConfigScene:update()
|
||||
@@ -58,7 +59,7 @@ function ConfigScene:render()
|
||||
if not ConfigScene.options[self.highlight][3] then
|
||||
love.graphics.rectangle("fill", 25, 98 + self.highlight * 20, 170, 22)
|
||||
else
|
||||
love.graphics.rectangle("fill", 65 + (1+self.highlight-#self.options) * 300, 322, 215, 33)
|
||||
love.graphics.rectangle("fill", 65 + (1+self.highlight-#self.options) * 300, 342, 215, 33)
|
||||
end
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
@@ -75,8 +76,8 @@ function ConfigScene:render()
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.print("SFX Volume: " .. math.floor(self.sfxSlider:getValue()) .. "%", 75, 325)
|
||||
love.graphics.print("BGM Volume: " .. math.floor(self.bgmSlider:getValue()) .. "%", 375, 325)
|
||||
love.graphics.print("SFX Volume: " .. math.floor(self.sfxSlider:getValue()) .. "%", 75, 345)
|
||||
love.graphics.print("BGM Volume: " .. math.floor(self.bgmSlider:getValue()) .. "%", 375, 345)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.75)
|
||||
self.sfxSlider:draw()
|
||||
|
||||
@@ -41,14 +41,14 @@ function ModeSelectScene:render()
|
||||
elseif self.menu_state.select == "ruleset" then
|
||||
love.graphics.setColor(1, 1, 1, 0.25)
|
||||
end
|
||||
love.graphics.rectangle("fill", 20, 258, 240, 22)
|
||||
love.graphics.rectangle("fill", 20, 198, 240, 22)
|
||||
|
||||
if self.menu_state.select == "mode" then
|
||||
love.graphics.setColor(1, 1, 1, 0.25)
|
||||
elseif self.menu_state.select == "ruleset" then
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
end
|
||||
love.graphics.rectangle("fill", 340, 258, 200, 22)
|
||||
love.graphics.rectangle("fill", 340, 198, 200, 22)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
@@ -56,13 +56,36 @@ function ModeSelectScene:render()
|
||||
|
||||
love.graphics.setFont(font_3x5_2)
|
||||
for idx, mode in pairs(game_modes) do
|
||||
if(idx >= self.menu_state.mode-9 and idx <= self.menu_state.mode+9) then
|
||||
love.graphics.printf(mode.name, 40, (260 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left")
|
||||
if(idx >= self.menu_state.mode-6 and idx <= self.menu_state.mode+6) then
|
||||
love.graphics.printf(mode.name, 40, (200 - 20*(self.menu_state.mode)) + 20 * idx, 200, "left")
|
||||
end
|
||||
end
|
||||
for idx, ruleset in pairs(rulesets) do
|
||||
if(idx >= self.menu_state.ruleset-9 and idx <= self.menu_state.ruleset+9) then
|
||||
love.graphics.printf(ruleset.name, 360, (260 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left")
|
||||
if(idx >= self.menu_state.ruleset-6 and idx <= self.menu_state.ruleset+6) then
|
||||
love.graphics.printf(ruleset.name, 360, (200 - 20*(self.menu_state.ruleset)) + 20 * idx, 160, "left")
|
||||
end
|
||||
end
|
||||
|
||||
-- mode description and highscore
|
||||
for midx, mode in pairs(game_modes) do
|
||||
for ridx, ruleset in pairs(rulesets) do
|
||||
if (midx == self.menu_state.mode) and (ridx == self.menu_state.ruleset) then
|
||||
love.graphics.printf(
|
||||
"Mode Description:\n\n" .. mode.tagline, 20, 350, 200, "left"
|
||||
)
|
||||
love.graphics.printf(
|
||||
ruleset.name .. " Highscore:", 240, 350, 200, "right"
|
||||
)
|
||||
local highscore_string = ""
|
||||
if highscores[mode.hash .. "-" .. ruleset.hash] then
|
||||
for k, v in ipairs(highscores[mode.hash .. "-" .. ruleset.hash]) do
|
||||
highscore_string = highscore_string .. k .. ": " .. v .. "\n"
|
||||
end
|
||||
else
|
||||
highscore_string = "You don't have any highscores yet!"
|
||||
end
|
||||
love.graphics.printf(highscore_string, 450, 350, 200, "left")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,6 +9,7 @@ TuningScene.options = {
|
||||
-- Serves as a reference for the options available in the menu. Format: {name in config, name as displayed if applicable, slider name}
|
||||
{"das", "DAS", "dasSlider"},
|
||||
{"arr", "ARR", "arrSlider"},
|
||||
{"dcd", "DCD", "dcdSlider"},
|
||||
}
|
||||
|
||||
local optioncount = #TuningScene.options
|
||||
@@ -20,13 +21,15 @@ function TuningScene:new()
|
||||
})
|
||||
self.highlight = 1
|
||||
|
||||
self.dasSlider = newSlider(290, 225, 400, config.das, 0, 20, function(v) config.das = math.floor(v) end, {width=20})
|
||||
self.arrSlider = newSlider(290, 325, 400, config.arr, 0, 6, function(v) config.arr = math.floor(v) end, {width=20})
|
||||
self.dasSlider = newSlider(290, 225, 400, config.das, 0, 20, function(v) config.das = math.floor(v) end, {width=20, knob="circle", track="roundrect"})
|
||||
self.arrSlider = newSlider(290, 300, 400, config.arr, 0, 6, function(v) config.arr = math.floor(v) end, {width=20, knob="circle", track="roundrect"})
|
||||
self.dcdSlider = newSlider(290, 375, 400, config.dcd, 0, 6, function(v) config.dcd = math.floor(v) end, {width=20, knob="circle", track="roundrect"})
|
||||
end
|
||||
|
||||
function TuningScene:update()
|
||||
self.dasSlider:update()
|
||||
self.arrSlider:update()
|
||||
self.arrSlider:update()
|
||||
self.dcdSlider:update()
|
||||
end
|
||||
|
||||
function TuningScene:render()
|
||||
@@ -38,7 +41,7 @@ function TuningScene:render()
|
||||
)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
love.graphics.rectangle("fill", 75, 73 + self.highlight * 100, 400, 33)
|
||||
love.graphics.rectangle("fill", 75, 98 + self.highlight * 75, 400, 33)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
@@ -50,11 +53,13 @@ function TuningScene:render()
|
||||
|
||||
love.graphics.setFont(font_3x5_3)
|
||||
love.graphics.print("Delayed Auto-Shift (DAS): " .. math.floor(self.dasSlider:getValue()) .. "F", 80, 175)
|
||||
love.graphics.print("Auto-Repeat Rate (ARR): " .. math.floor(self.arrSlider:getValue()) .. "F", 80, 275)
|
||||
love.graphics.print("Auto-Repeat Rate (ARR): " .. math.floor(self.arrSlider:getValue()) .. "F", 80, 250)
|
||||
love.graphics.print("DAS Cut Delay (DCD): " .. math.floor(self.dcdSlider:getValue()) .. "F", 80, 325)
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 0.75)
|
||||
self.dasSlider:draw()
|
||||
self.arrSlider:draw()
|
||||
self.arrSlider:draw()
|
||||
self.dcdSlider:draw()
|
||||
end
|
||||
|
||||
function TuningScene:onInputPress(e)
|
||||
|
||||
@@ -6,13 +6,15 @@ local empty = { skin = "", colour = "" }
|
||||
local oob = { skin = "", colour = "" }
|
||||
local block = { skin = "2tie", colour = "A" }
|
||||
|
||||
function Grid:new()
|
||||
function Grid:new(width, height)
|
||||
self.grid = {}
|
||||
self.grid_age = {}
|
||||
for y = 1, 24 do
|
||||
self.width = width
|
||||
self.height = height
|
||||
for y = 1, self.height do
|
||||
self.grid[y] = {}
|
||||
self.grid_age[y] = {}
|
||||
for x = 1, 10 do
|
||||
for x = 1, self.width do
|
||||
self.grid[y][x] = empty
|
||||
self.grid_age[y][x] = 0
|
||||
end
|
||||
@@ -20,8 +22,8 @@ function Grid:new()
|
||||
end
|
||||
|
||||
function Grid:clear()
|
||||
for y = 1, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 1, self.height do
|
||||
for x = 1, self.width do
|
||||
self.grid[y][x] = empty
|
||||
self.grid_age[y][x] = 0
|
||||
end
|
||||
@@ -29,7 +31,7 @@ function Grid:clear()
|
||||
end
|
||||
|
||||
function Grid:getCell(x, y)
|
||||
if x < 1 or x > 10 or y > 24 then return oob
|
||||
if x < 1 or x > self.width or y > self.height then return oob
|
||||
elseif y < 1 then return empty
|
||||
else return self.grid[y][x]
|
||||
end
|
||||
@@ -98,18 +100,20 @@ end
|
||||
|
||||
function Grid:getClearedRowCount()
|
||||
local count = 0
|
||||
for row = 1, 24 do
|
||||
local cleared_row_table = {}
|
||||
for row = 1, self.height do
|
||||
if self:isRowFull(row) then
|
||||
count = count + 1
|
||||
table.insert(cleared_row_table, row)
|
||||
end
|
||||
end
|
||||
return count
|
||||
return count, cleared_row_table
|
||||
end
|
||||
|
||||
function Grid:markClearedRows()
|
||||
for row = 1, 24 do
|
||||
for row = 1, self.height do
|
||||
if self:isRowFull(row) then
|
||||
for x = 1, 10 do
|
||||
for x = 1, self.width do
|
||||
self.grid[row][x] = {
|
||||
skin = self.grid[row][x].skin,
|
||||
colour = "X"
|
||||
@@ -122,66 +126,52 @@ function Grid:markClearedRows()
|
||||
end
|
||||
|
||||
function Grid:clearClearedRows()
|
||||
for row = 1, 24 do
|
||||
for row = 1, self.height do
|
||||
if self:isRowFull(row) then
|
||||
for above_row = row, 2, -1 do
|
||||
self.grid[above_row] = self.grid[above_row - 1]
|
||||
self.grid_age[above_row] = self.grid_age[above_row - 1]
|
||||
end
|
||||
self.grid[1] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
|
||||
self.grid_age[1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
self.grid[1] = {}
|
||||
self.grid_age[1] = {}
|
||||
for i = 1, self.width do
|
||||
self.grid[1][i] = empty
|
||||
self.grid_age[1][i] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Grid:copyBottomRow()
|
||||
for row = 1, 23 do
|
||||
for row = 1, self.height - 1 do
|
||||
self.grid[row] = self.grid[row+1]
|
||||
self.grid_age[row] = self.grid_age[row+1]
|
||||
end
|
||||
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
|
||||
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
for col = 1, 10 do
|
||||
self.grid[24][col] = (self.grid[23][col] == empty) and empty or block
|
||||
self.grid[self.height] = {}
|
||||
self.grid_age[self.height] = {}
|
||||
for i = 1, self.width do
|
||||
self.grid[self.height][i] = (self.grid[self.height - 1][i] == empty) and empty or block
|
||||
self.grid_age[self.height][i] = 0
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Grid:garbageRise(row_vals)
|
||||
for row = 1, 23 do
|
||||
self.grid[row] = self.grid[row+1]
|
||||
self.grid_age[row] = self.grid_age[row+1]
|
||||
end
|
||||
self.grid[24] = {empty, empty, empty, empty, empty, empty, empty, empty, empty, empty}
|
||||
self.grid_age[24] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
for col = 1, 10 do
|
||||
self.grid[24][col] = (row_vals[col] == "e") and empty or block
|
||||
for row = 1, self.height - 1 do
|
||||
self.grid[row] = self.grid[row+1]
|
||||
self.grid_age[row] = self.grid_age[row+1]
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:applyFourWide()
|
||||
for row = 1, 24 do
|
||||
local x = self.grid[row]
|
||||
x[1] = x[1]~=block and block or x[1]
|
||||
x[2] = x[2]~=block and block or x[2]
|
||||
x[3] = x[3]~=block and block or x[3]
|
||||
x[8] = x[8]~=block and block or x[8]
|
||||
x[9] = x[9]~=block and block or x[9]
|
||||
x[10] = x[10]~=block and block or x[10]
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:applyCeiling(lines)
|
||||
for row = 1, lines do
|
||||
for col = 1, 9 do
|
||||
self.grid[row][col] = block
|
||||
end
|
||||
self.grid[self.height] = {}
|
||||
self.grid_age[self.height] = {}
|
||||
for i = 1, self.width do
|
||||
self.grid[self.height][i] = (row_vals[i] == "e") and empty or block
|
||||
self.grid_age[self.height][i] = 0
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:clearSpecificRow(row)
|
||||
for col = 1, 10 do
|
||||
for col = 1, self.width do
|
||||
self.grid[row][col] = empty
|
||||
end
|
||||
end
|
||||
@@ -195,7 +185,7 @@ function Grid:applyPiece(piece)
|
||||
for index, offset in pairs(offsets) do
|
||||
x = piece.position.x + offset.x
|
||||
y = piece.position.y + offset.y
|
||||
if y + 1 > 0 and y < 24 then
|
||||
if y + 1 > 0 and y < self.height then
|
||||
self.grid[y+1][x+1] = {
|
||||
skin = piece.skin,
|
||||
colour = piece.colour
|
||||
@@ -211,7 +201,7 @@ function Grid:applyBigPiece(piece)
|
||||
y = piece.position.y + offset.y
|
||||
for a = 1, 2 do
|
||||
for b = 1, 2 do
|
||||
if y*2+a > 0 then
|
||||
if y*2+a > 0 and y*2 < self.height then
|
||||
self.grid[y*2+a][x*2+b] = {
|
||||
skin = piece.skin,
|
||||
colour = piece.colour
|
||||
@@ -223,8 +213,8 @@ function Grid:applyBigPiece(piece)
|
||||
end
|
||||
|
||||
function Grid:checkForBravo(cleared_row_count)
|
||||
for i = 0, 23 - cleared_row_count do
|
||||
for j = 0, 9 do
|
||||
for i = 0, self.height - 1 - cleared_row_count do
|
||||
for j = 0, self.width - 1 do
|
||||
if self:isOccupied(j, i) then return false end
|
||||
end
|
||||
end
|
||||
@@ -232,9 +222,9 @@ function Grid:checkForBravo(cleared_row_count)
|
||||
end
|
||||
|
||||
function Grid:checkStackHeight()
|
||||
for i = 0, 23 do
|
||||
for j = 0, 9 do
|
||||
if self:isOccupied(j, i) then return 24 - i end
|
||||
for i = 0, self.height - 1 do
|
||||
for j = 0, self.width - 1 do
|
||||
if self:isOccupied(j, i) then return self.height - i end
|
||||
end
|
||||
end
|
||||
return 0
|
||||
@@ -275,8 +265,8 @@ function Grid:checkSecretGrade()
|
||||
end
|
||||
|
||||
function Grid:hasGemBlocks()
|
||||
for y = 1, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 1, self.height do
|
||||
for x = 1, self.width do
|
||||
if self.grid[y][x].skin == "gem" then
|
||||
return true
|
||||
end
|
||||
@@ -287,16 +277,16 @@ end
|
||||
|
||||
function Grid:mirror()
|
||||
local new_grid = {}
|
||||
for y = 1, 24 do
|
||||
for y = 1, self.height do
|
||||
new_grid[y] = {}
|
||||
for x = 1, 10 do
|
||||
for x = 1, self.width do
|
||||
new_grid[y][x] = empty
|
||||
end
|
||||
end
|
||||
|
||||
for y = 1, 24 do
|
||||
for x = 1, 10 do
|
||||
new_grid[y][x] = self.grid[y][11 - x]
|
||||
for y = 1, self.height do
|
||||
for x = 1, self.width do
|
||||
new_grid[y][x] = self.grid[y][self.width + 1 - x]
|
||||
end
|
||||
end
|
||||
self.grid = new_grid
|
||||
@@ -311,9 +301,83 @@ function Grid:applyMap(map)
|
||||
end
|
||||
end
|
||||
|
||||
-- inefficient algorithm for squares
|
||||
function Grid:markSquares()
|
||||
-- goes up by 1 for silver, 2 for gold
|
||||
local square_count = 0
|
||||
for i = 1, 2 do
|
||||
for y = 5, self.height - 3 do
|
||||
for x = 1, self.width - 3 do
|
||||
local age_table = {}
|
||||
local age_count = 0
|
||||
local colour_table = {}
|
||||
local is_square = true
|
||||
for j = 0, 3 do
|
||||
for k = 0, 3 do
|
||||
if self.grid[y+j][x+k].skin == "" or self.grid[y+j][x+k].skin == "square" then
|
||||
is_square = false
|
||||
end
|
||||
if age_table[self.grid_age[y+j][x+k]] == nil then
|
||||
age_table[self.grid_age[y+j][x+k]] = 1
|
||||
age_count = age_count + 1
|
||||
else
|
||||
age_table[self.grid_age[y+j][x+k]] = age_table[self.grid_age[y+j][x+k]] + 1
|
||||
end
|
||||
if age_count > 4 or age_table[self.grid_age[y+j][x+k]] > 4 then
|
||||
is_square = false
|
||||
end
|
||||
if not table.contains(colour_table, self.grid[y+j][x+k].colour) then
|
||||
table.insert(colour_table, self.grid[y+j][x+k].colour)
|
||||
end
|
||||
end
|
||||
end
|
||||
if is_square then
|
||||
if i == 1 and #colour_table == 1 then
|
||||
for j = 0, 3 do
|
||||
for k = 0, 3 do
|
||||
self.grid[y+j][x+k].colour = "Y"
|
||||
self.grid[y+j][x+k].skin = "square"
|
||||
end
|
||||
end
|
||||
square_count = square_count + 2
|
||||
elseif i == 2 then
|
||||
for j = 0, 3 do
|
||||
for k = 0, 3 do
|
||||
self.grid[y+j][x+k].colour = "F"
|
||||
self.grid[y+j][x+k].skin = "square"
|
||||
end
|
||||
|
||||
end
|
||||
square_count = square_count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return square_count
|
||||
end
|
||||
|
||||
-- square scan
|
||||
function Grid:scanForSquares()
|
||||
local table = {}
|
||||
for row = 1, self.height do
|
||||
local silver = 0
|
||||
local gold = 0
|
||||
for col = 1, self.width do
|
||||
local colour = self.grid[row][col].colour
|
||||
if self.grid[row][col].skin == "square" then
|
||||
if colour == "Y" then gold = gold + 1
|
||||
else silver = silver + 1 end
|
||||
end
|
||||
end
|
||||
table[row] = gold * 2.5 + silver * 1.25
|
||||
end
|
||||
return table
|
||||
end
|
||||
|
||||
function Grid:update()
|
||||
for y = 1, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 1, self.height do
|
||||
for x = 1, self.width do
|
||||
if self.grid[y][x] ~= empty then
|
||||
self.grid_age[y][x] = self.grid_age[y][x] + 1
|
||||
end
|
||||
@@ -322,8 +386,8 @@ function Grid:update()
|
||||
end
|
||||
|
||||
function Grid:draw()
|
||||
for y = 5, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 5, self.height do
|
||||
for x = 1, self.width do
|
||||
if self.grid[y][x] ~= empty then
|
||||
if self.grid_age[y][x] < 2 then
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
@@ -344,14 +408,14 @@ function Grid:draw()
|
||||
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
|
||||
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
|
||||
end
|
||||
if y < 24 and self.grid[y+1][x] == empty or
|
||||
(y + 1 > 24 or self.grid[y+1][x].colour == "X") then
|
||||
if y < self.height and self.grid[y+1][x] == empty or
|
||||
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then
|
||||
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
|
||||
end
|
||||
if x > 1 and self.grid[y][x-1] == empty then
|
||||
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
||||
end
|
||||
if x < 10 and self.grid[y][x+1] == empty then
|
||||
if x < self.width and self.grid[y][x+1] == empty then
|
||||
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
|
||||
end
|
||||
end
|
||||
@@ -361,8 +425,8 @@ function Grid:draw()
|
||||
end
|
||||
|
||||
function Grid:drawOutline()
|
||||
for y = 5, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 5, self.height do
|
||||
for x = 1, self.width do
|
||||
if self.grid[y][x].colour == "X" then
|
||||
love.graphics.setColor(0.5, 0.5, 0.5, 1 - self.grid_age[y][x] / 15)
|
||||
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
|
||||
@@ -373,14 +437,14 @@ function Grid:drawOutline()
|
||||
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
|
||||
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
|
||||
end
|
||||
if y < 24 and self.grid[y+1][x] == empty or
|
||||
(y + 1 > 24 or self.grid[y+1][x].colour == "X") then
|
||||
if y < self.height and self.grid[y+1][x] == empty or
|
||||
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then
|
||||
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
|
||||
end
|
||||
if x > 1 and self.grid[y][x-1] == empty then
|
||||
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
||||
end
|
||||
if x < 10 and self.grid[y][x+1] == empty then
|
||||
if x < self.width and self.grid[y][x+1] == empty then
|
||||
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
|
||||
end
|
||||
end
|
||||
@@ -391,8 +455,8 @@ end
|
||||
function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_flash, brightness)
|
||||
lock_flash = lock_flash == nil and true or lock_flash
|
||||
brightness = brightness == nil and 0.5 or brightness
|
||||
for y = 5, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 5, self.height do
|
||||
for x = 1, self.width do
|
||||
if self.grid[y][x] ~= empty then
|
||||
if self.grid[y][x].colour == "X" then
|
||||
opacity = 1 - self.grid_age[y][x] / 15
|
||||
@@ -410,14 +474,14 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
|
||||
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
|
||||
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
|
||||
end
|
||||
if y < 24 and self.grid[y+1][x] == empty or
|
||||
(y + 1 > 24 or self.grid[y+1][x].colour == "X") then
|
||||
if y < self.height and self.grid[y+1][x] == empty or
|
||||
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then
|
||||
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
|
||||
end
|
||||
if x > 1 and self.grid[y][x-1] == empty then
|
||||
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
||||
end
|
||||
if x < 10 and self.grid[y][x+1] == empty then
|
||||
if x < self.width and self.grid[y][x+1] == empty then
|
||||
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
|
||||
end
|
||||
end
|
||||
@@ -436,8 +500,8 @@ function Grid:drawCustom(colour_function, gamestate)
|
||||
|
||||
gamestate: the gamemode instance itself to pass in colour_function
|
||||
]]
|
||||
for y = 5, 24 do
|
||||
for x = 1, 10 do
|
||||
for y = 5, self.height do
|
||||
for x = 1, self.width do
|
||||
local block = self.grid[y][x]
|
||||
if block ~= empty then
|
||||
local R, G, B, A, outline = colour_function(gamestate, block, x, y, self.grid_age[y][x])
|
||||
@@ -452,14 +516,14 @@ function Grid:drawCustom(colour_function, gamestate)
|
||||
if y > 1 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
|
||||
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
|
||||
end
|
||||
if y < 24 and self.grid[y+1][x] == empty or
|
||||
(y + 1 > 24 or self.grid[y+1][x].colour == "X") then
|
||||
if y < self.height and self.grid[y+1][x] == empty or
|
||||
(y + 1 > self.height or self.grid[y+1][x].colour == "X") then
|
||||
love.graphics.line(48.0+x*16, 16.5+y*16, 64.0+x*16, 16.5+y*16)
|
||||
end
|
||||
if x > 1 and self.grid[y][x-1] == empty then
|
||||
love.graphics.line(47.5+x*16, -0.0+y*16, 47.5+x*16, 16.0+y*16)
|
||||
end
|
||||
if x < 10 and self.grid[y][x+1] == empty then
|
||||
if x < self.width and self.grid[y][x+1] == empty then
|
||||
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -13,7 +13,7 @@ local GameMode = Object:extend()
|
||||
GameMode.rollOpacityFunction = function(age) return 0 end
|
||||
|
||||
function GameMode:new(secret_inputs)
|
||||
self.grid = Grid()
|
||||
self.grid = Grid(10, 24)
|
||||
self.randomizer = Randomizer()
|
||||
self.piece = nil
|
||||
self.ready_frames = 100
|
||||
@@ -22,6 +22,7 @@ function GameMode:new(secret_inputs)
|
||||
self.score = 0
|
||||
self.level = 0
|
||||
self.lines = 0
|
||||
self.squares = 0
|
||||
self.drop_bonus = 0
|
||||
self.are = 0
|
||||
self.lcd = 0
|
||||
@@ -46,6 +47,8 @@ function GameMode:new(secret_inputs)
|
||||
self.big_mode = false
|
||||
self.irs = true
|
||||
self.ihs = true
|
||||
self.square_mode = false
|
||||
self.immobile_spin_bonus = false
|
||||
self.rpc_details = "In game"
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
@@ -72,6 +75,7 @@ function GameMode:getLineARE() return 25 end
|
||||
function GameMode:getLockDelay() return 30 end
|
||||
function GameMode:getLineClearDelay() return 40 end
|
||||
function GameMode:getDasLimit() return 15 end
|
||||
function GameMode:getDasCutDelay() return 0 end
|
||||
|
||||
function GameMode:getNextPiece(ruleset)
|
||||
return {
|
||||
@@ -105,14 +109,10 @@ function GameMode:initialize(ruleset, secret_inputs)
|
||||
end
|
||||
|
||||
function GameMode:update(inputs, ruleset)
|
||||
if self.game_over then
|
||||
if self.game_over or self.completed then
|
||||
self.game_over_frames = self.game_over_frames + 1
|
||||
if self.game_over_frames >= 60 then
|
||||
self.completed = true
|
||||
end
|
||||
return
|
||||
end
|
||||
if self.completed then return end
|
||||
|
||||
if config.gamesettings.diagonal_input == 2 then
|
||||
if inputs["left"] or inputs["right"] then
|
||||
@@ -127,16 +127,34 @@ function GameMode:update(inputs, ruleset)
|
||||
-- advance one frame
|
||||
if self:advanceOneFrame(inputs, ruleset) == false then return end
|
||||
|
||||
self:chargeDAS(inputs, self:getDasLimit(), self.getARR())
|
||||
self:chargeDAS(inputs, self:getDasLimit(), self:getARR())
|
||||
|
||||
-- set attempt flags
|
||||
if inputs["left"] or inputs["right"] then self:onAttemptPieceMove(self.piece) end
|
||||
if inputs["left"] or inputs["right"] then
|
||||
self:onAttemptPieceMove(self.piece)
|
||||
if self.immobile_spin_bonus and self.piece ~= nil then
|
||||
if not self.piece:isMoveBlocked(self.grid, { x=-1, y=0 }) and
|
||||
not self.piece:isMoveBlocked(self.grid, { x=1, y=0 }) then
|
||||
self.piece.spin = false
|
||||
end
|
||||
end
|
||||
end
|
||||
if
|
||||
inputs["rotate_left"] or inputs["rotate_right"] or
|
||||
inputs["rotate_left2"] or inputs["rotate_right2"] or
|
||||
inputs["rotate_180"]
|
||||
then
|
||||
self:onAttemptPieceRotate(self.piece)
|
||||
if self.immobile_spin_bonus and self.piece ~= nil then
|
||||
if self.piece:isDropBlocked(self.grid) and
|
||||
self.piece:isMoveBlocked(self.grid, { x=-1, y=0 }) and
|
||||
self.piece:isMoveBlocked(self.grid, { x=1, y=0 }) and
|
||||
self.piece:isMoveBlocked(self.grid, { x=0, y=-1 }) then
|
||||
self.piece.spin = true
|
||||
else
|
||||
self.piece.spin = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.piece == nil then
|
||||
@@ -160,7 +178,9 @@ function GameMode:update(inputs, ruleset)
|
||||
self.hard_drop_locked = false
|
||||
end
|
||||
|
||||
-- diff vars to use in checks
|
||||
local piece_y = self.piece.position.y
|
||||
local piece_rot = self.piece.rotation
|
||||
|
||||
ruleset:processPiece(
|
||||
inputs, self.piece, self.grid, self:getGravity(), self.prev_inputs,
|
||||
@@ -170,6 +190,22 @@ function GameMode:update(inputs, ruleset)
|
||||
)
|
||||
|
||||
local piece_dy = self.piece.position.y - piece_y
|
||||
local piece_drot = self.piece.rotation - piece_rot
|
||||
|
||||
-- das cut
|
||||
if (
|
||||
(piece_dy ~= 0 and (inputs.up or inputs.down)) or
|
||||
(piece_drot ~= 0 and (
|
||||
inputs.rotate_left or inputs.rotate_right or
|
||||
inputs.rotate_left2 or inputs.rotate_right2 or
|
||||
inputs.rotate_180
|
||||
))
|
||||
) then
|
||||
self.das.frames = math.max(
|
||||
self.das.frames - self:getDasCutDelay(),
|
||||
-self:getDasCutDelay()
|
||||
)
|
||||
end
|
||||
|
||||
if inputs["up"] == true and
|
||||
self.piece:isDropBlocked(self.grid) and
|
||||
@@ -193,13 +229,15 @@ function GameMode:update(inputs, ruleset)
|
||||
|
||||
if self.piece.locked == true then
|
||||
self.grid:applyPiece(self.piece)
|
||||
self.grid:markClearedRows()
|
||||
if self.square_mode then
|
||||
self.squares = self.squares + self.grid:markSquares()
|
||||
end
|
||||
|
||||
local cleared_row_count = self.grid:getClearedRowCount()
|
||||
|
||||
self:onPieceLock(self.piece, cleared_row_count)
|
||||
self:updateScore(self.level, self.drop_bonus, cleared_row_count)
|
||||
|
||||
self.grid:markClearedRows()
|
||||
self.piece = nil
|
||||
if self.enable_hold then
|
||||
self.held = false
|
||||
@@ -263,6 +301,15 @@ end
|
||||
|
||||
function GameMode:onGameOver()
|
||||
switchBGM(nil)
|
||||
love.graphics.setColor(0, 0, 0, 1 - 2 ^ (-self.game_over_frames / 30))
|
||||
love.graphics.rectangle(
|
||||
"fill", 64, 80,
|
||||
16 * self.grid.width, 16 * (self.grid.height - 4)
|
||||
)
|
||||
end
|
||||
|
||||
function GameMode:onGameComplete()
|
||||
self:onGameOver()
|
||||
end
|
||||
|
||||
-- DAS functions
|
||||
@@ -393,14 +440,15 @@ function GameMode:processDelays(inputs, ruleset, drop_speed)
|
||||
end
|
||||
|
||||
function GameMode:initializeOrHold(inputs, ruleset)
|
||||
if self.ihs and self.enable_hold and inputs["hold"] == true then
|
||||
if (
|
||||
self.frames == 0 or (ruleset.are and self:getARE() ~= 0) and self.ihs or false
|
||||
) and self.enable_hold and inputs["hold"] == true then
|
||||
self:hold(inputs, ruleset, true)
|
||||
else
|
||||
self:initializeNextPiece(inputs, ruleset, self.next_queue[1])
|
||||
end
|
||||
self:onPieceEnter()
|
||||
if not self.grid:canPlacePiece(self.piece) then
|
||||
self:onGameOver()
|
||||
self.game_over = true
|
||||
end
|
||||
end
|
||||
@@ -437,7 +485,10 @@ function GameMode:initializeNextPiece(inputs, ruleset, piece_data, generate_next
|
||||
self.prev_inputs, self.move,
|
||||
self:getLockDelay(), self:getDropSpeed(),
|
||||
self.lock_drop, self.lock_hard_drop, self.big_mode,
|
||||
self.irs, self.buffer_hard_drop, self.buffer_soft_drop,
|
||||
(
|
||||
self.frames == 0 or (ruleset.are and self:getARE() ~= 0)
|
||||
) and self.irs or false,
|
||||
self.buffer_hard_drop, self.buffer_soft_drop,
|
||||
self.lock_on_hard_drop, self.lock_on_soft_drop
|
||||
)
|
||||
if self.piece:isDropBlocked(self.grid) and
|
||||
@@ -501,8 +552,8 @@ function GameMode:drawNextQueue(ruleset)
|
||||
local colourscheme = ({ruleset.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
|
||||
function drawPiece(piece, skin, offsets, pos_x, pos_y)
|
||||
for index, offset in pairs(offsets) do
|
||||
local x = offset.x + ruleset.spawn_positions[piece].x
|
||||
local y = offset.y + 4.7
|
||||
local x = offset.x + ruleset.draw_offsets[piece].x + ruleset.spawn_positions[piece].x
|
||||
local y = offset.y + ruleset.draw_offsets[piece].y + 4.7
|
||||
love.graphics.draw(blocks[skin][colourscheme[piece]], pos_x+x*16, pos_y+y*16)
|
||||
end
|
||||
end
|
||||
@@ -518,8 +569,7 @@ function GameMode:drawNextQueue(ruleset)
|
||||
end
|
||||
end
|
||||
if self.hold_queue ~= nil and self.enable_hold then
|
||||
local hold_color = self.held and 0.6 or 1
|
||||
self:setHoldOpacity(1, hold_color)
|
||||
self:setHoldOpacity()
|
||||
drawPiece(
|
||||
self.hold_queue.shape,
|
||||
self.hold_queue.skin,
|
||||
@@ -530,15 +580,13 @@ function GameMode:drawNextQueue(ruleset)
|
||||
return false
|
||||
end
|
||||
|
||||
function GameMode:setNextOpacity(i, j)
|
||||
i = i ~= nil and i or 1
|
||||
j = j ~= nil and j or 1
|
||||
love.graphics.setColor(j, j, j, i)
|
||||
function GameMode:setNextOpacity(i)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
function GameMode:setHoldOpacity(i, j)
|
||||
i = i ~= nil and i or 1
|
||||
j = j ~= nil and j or 1
|
||||
love.graphics.setColor(j, j, j, i)
|
||||
|
||||
function GameMode:setHoldOpacity()
|
||||
local colour = self.held and 0.6 or 1
|
||||
love.graphics.setColor(colour, colour, colour, 1)
|
||||
end
|
||||
|
||||
function GameMode:drawScoringInfo()
|
||||
@@ -578,7 +626,8 @@ function GameMode:sectionColourFunction(section)
|
||||
return { 1, 1, 1, 1 }
|
||||
end
|
||||
|
||||
function GameMode:drawSectionTimesWithSecondary(current_section)
|
||||
function GameMode:drawSectionTimesWithSecondary(current_section, section_limit)
|
||||
section_limit = section_limit or math.huge
|
||||
local section_x = 530
|
||||
local section_secondary_x = 440
|
||||
|
||||
@@ -603,7 +652,9 @@ function GameMode:drawSectionTimesWithSecondary(current_section)
|
||||
current_x = section_secondary_x
|
||||
end
|
||||
|
||||
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left")
|
||||
if current_section <= section_limit then
|
||||
love.graphics.printf(formatTime(self.frames - self.section_start_time), current_x, 40 + 20 * current_section, 90, "left")
|
||||
end
|
||||
end
|
||||
|
||||
function GameMode:drawSectionTimesWithSplits(current_section)
|
||||
|
||||
@@ -256,8 +256,10 @@ end
|
||||
local function getSectionForLevel(level)
|
||||
if level < 2001 then
|
||||
return math.floor(level / 100) + 1
|
||||
else
|
||||
elseif level < 2020 then
|
||||
return 20
|
||||
else
|
||||
return 21
|
||||
end
|
||||
end
|
||||
|
||||
@@ -442,7 +444,7 @@ function Marathon2020Game:drawScoringInfo()
|
||||
love.graphics.printf("GRADE PTS.", text_x, 200, 90, "left")
|
||||
love.graphics.printf("LEVEL", text_x, 320, 40, "left")
|
||||
|
||||
self:drawSectionTimesWithSecondary(current_section)
|
||||
self:drawSectionTimesWithSecondary(current_section, 20)
|
||||
|
||||
if (self.cool_timer > 0) then
|
||||
love.graphics.printf("COOL!!", 64, 400, 160, "center")
|
||||
|
||||
@@ -2,6 +2,7 @@ require 'funcs'
|
||||
|
||||
local GameMode = require 'tetris.modes.gamemode'
|
||||
local Piece = require 'tetris.components.piece'
|
||||
local Grid = require 'tetris.components.grid'
|
||||
|
||||
local History4RollsRandomizer = require 'tetris.randomizers.history_4rolls'
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ function MarathonA2Game:new()
|
||||
self.section_start_time = 0
|
||||
self.section_times = { [0] = 0 }
|
||||
self.section_tetrises = { [0] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
self.tetris_count = 0
|
||||
|
||||
self.SGnames = {
|
||||
"9", "8", "7", "6", "5", "4", "3", "2", "1",
|
||||
@@ -130,6 +131,9 @@ end
|
||||
function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
if not self.clear then
|
||||
self:updateGrade(cleared_lines)
|
||||
if cleared_lines >= 4 then
|
||||
self.tetris_count = self.tetris_count + 1
|
||||
end
|
||||
if self.grid:checkForBravo(cleared_lines) then self.bravo = 4 else self.bravo = 1 end
|
||||
if cleared_lines > 0 then
|
||||
self.combo = self.combo + (cleared_lines - 1) * 2
|
||||
@@ -145,6 +149,7 @@ function MarathonA2Game:updateScore(level, drop_bonus, cleared_lines)
|
||||
end
|
||||
|
||||
function MarathonA2Game:onLineClear(cleared_row_count)
|
||||
self:updateSectionTimes(self.level, self.level + cleared_row_count)
|
||||
self.level = math.min(self.level + cleared_row_count, 999)
|
||||
if self.level == 999 and not self.clear then
|
||||
self.clear = true
|
||||
@@ -164,6 +169,9 @@ function MarathonA2Game:updateSectionTimes(old_level, new_level)
|
||||
section_time = self.frames - self.section_start_time
|
||||
self.section_times[math.floor(old_level / 100)] = section_time
|
||||
self.section_start_time = self.frames
|
||||
self.section_tetrises[math.floor(old_level / 100)] = self.tetris_count
|
||||
self.tetris_count = 0
|
||||
print(self.section_tetrises[math.floor(old_level / 100)])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -231,14 +239,16 @@ local grade_conversion = {
|
||||
17, 18, 19
|
||||
}
|
||||
|
||||
function MarathonA2Game:whilePieceActive()
|
||||
self.grade_point_decay_counter = self.grade_point_decay_counter + 1
|
||||
if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then
|
||||
self.grade_point_decay_counter = 0
|
||||
self.grade_points = math.max(0, self.grade_points - 1)
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA2Game:updateGrade(cleared_lines)
|
||||
if self.clear then return end
|
||||
if cleared_lines == 0 then
|
||||
self.grade_point_decay_counter = self.grade_point_decay_counter + 1
|
||||
if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then
|
||||
self.grade_point_decay_counter = 0
|
||||
self.grade_points = math.max(0, self.grade_points - 1)
|
||||
end
|
||||
if self.clear or cleared_lines == 0 then return
|
||||
else
|
||||
self.grade_points = self.grade_points + (
|
||||
math.ceil(
|
||||
|
||||
@@ -313,13 +313,16 @@ local grade_conversion = {
|
||||
17
|
||||
}
|
||||
|
||||
function MarathonA3Game:whilePieceActive()
|
||||
self.grade_point_decay_counter = self.grade_point_decay_counter + 1
|
||||
if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then
|
||||
self.grade_point_decay_counter = 0
|
||||
self.grade_points = math.max(0, self.grade_points - 1)
|
||||
end
|
||||
end
|
||||
|
||||
function MarathonA3Game:updateGrade(cleared_lines)
|
||||
if cleared_lines == 0 then
|
||||
self.grade_point_decay_counter = self.grade_point_decay_counter + 1
|
||||
if self.grade_point_decay_counter >= grade_point_decays[self.grade + 1] then
|
||||
self.grade_point_decay_counter = 0
|
||||
self.grade_points = math.max(0, self.grade_points - 1)
|
||||
end
|
||||
if cleared_lines == 0 then return
|
||||
else
|
||||
if self.clear then
|
||||
if self:qualifiesForMRoll() then
|
||||
@@ -424,8 +427,8 @@ function MarathonA3Game:drawScoringInfo()
|
||||
end
|
||||
|
||||
-- draw section time data
|
||||
current_section = math.floor(self.level / 100) + 1
|
||||
self:drawSectionTimesWithSecondary(current_section)
|
||||
current_section = self.level >= 999 and 11 or math.floor(self.level / 100) + 1
|
||||
self:drawSectionTimesWithSecondary(current_section, 10)
|
||||
--[[
|
||||
|
||||
section_x = 530
|
||||
|
||||
@@ -493,7 +493,7 @@ function SakuraGame:drawScoringInfo()
|
||||
if effects[self.current_map] then
|
||||
love.graphics.printf("EFFECT: " .. effects[self.current_map], 240, 300, 160, "left")
|
||||
end
|
||||
if self.randomizer.history then
|
||||
if self.used_randomizer.history then
|
||||
love.graphics.printf("RANDOM PIECES ACTIVE!", 240, 150, 200, "left")
|
||||
end
|
||||
|
||||
|
||||
@@ -66,24 +66,24 @@ function SurvivalA1Game:getGravity()
|
||||
end
|
||||
|
||||
local function getRankForScore(score)
|
||||
if score < 400 then return {rank = "9", next = 400}
|
||||
elseif score < 800 then return {rank = "8", next = 800}
|
||||
elseif score < 1400 then return {rank = "7", next = 1400}
|
||||
elseif score < 2000 then return {rank = "6", next = 2000}
|
||||
elseif score < 3500 then return {rank = "5", next = 3500}
|
||||
elseif score < 5500 then return {rank = "4", next = 5500}
|
||||
elseif score < 8000 then return {rank = "3", next = 8000}
|
||||
elseif score < 12000 then return {rank = "2", next = 12000}
|
||||
elseif score < 16000 then return {rank = "1", next = 16000}
|
||||
elseif score < 22000 then return {rank = "S1", next = 22000}
|
||||
elseif score < 30000 then return {rank = "S2", next = 30000}
|
||||
elseif score < 40000 then return {rank = "S3", next = 40000}
|
||||
elseif score < 52000 then return {rank = "S4", next = 52000}
|
||||
elseif score < 66000 then return {rank = "S5", next = 66000}
|
||||
elseif score < 82000 then return {rank = "S6", next = 82000}
|
||||
elseif score < 100000 then return {rank = "S7", next = 100000}
|
||||
elseif score < 120000 then return {rank = "S8", next = 120000}
|
||||
else return {rank = "S9", next = "???"}
|
||||
if score < 400 then return {rank = "9", next = 400, grade = 0}
|
||||
elseif score < 800 then return {rank = "8", next = 800, grade = 1}
|
||||
elseif score < 1400 then return {rank = "7", next = 1400, grade = 2}
|
||||
elseif score < 2000 then return {rank = "6", next = 2000, grade = 3}
|
||||
elseif score < 3500 then return {rank = "5", next = 3500, grade = 4}
|
||||
elseif score < 5500 then return {rank = "4", next = 5500, grade = 5}
|
||||
elseif score < 8000 then return {rank = "3", next = 8000, grade = 6}
|
||||
elseif score < 12000 then return {rank = "2", next = 12000, grade = 7}
|
||||
elseif score < 16000 then return {rank = "1", next = 16000, grade = 8}
|
||||
elseif score < 22000 then return {rank = "S1", next = 22000, grade = 9}
|
||||
elseif score < 30000 then return {rank = "S2", next = 30000, grade = 10}
|
||||
elseif score < 40000 then return {rank = "S3", next = 40000, grade = 11}
|
||||
elseif score < 52000 then return {rank = "S4", next = 52000, grade = 12}
|
||||
elseif score < 66000 then return {rank = "S5", next = 66000, grade = 13}
|
||||
elseif score < 82000 then return {rank = "S6", next = 82000, grade = 14}
|
||||
elseif score < 100000 then return {rank = "S7", next = 100000, grade = 15}
|
||||
elseif score < 120000 then return {rank = "S8", next = 120000, grade = 16}
|
||||
else return {rank = "S9", next = "???", grade = 17}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -208,10 +208,15 @@ end
|
||||
|
||||
function SurvivalA1Game:getHighscoreData()
|
||||
return {
|
||||
grade = self.grade,
|
||||
grade = (
|
||||
(self.gm_conditions["level300"] and
|
||||
self.gm_conditions["level500"] and
|
||||
self.gm_conditions["level999"]) and
|
||||
18 or getRankForScore(self.score).grade
|
||||
),
|
||||
frames = self.frames,
|
||||
score = self.score,
|
||||
level = self.level,
|
||||
frames = self.frames,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -19,26 +19,6 @@ ARS.colourscheme = {
|
||||
ARS.softdrop_lock = false
|
||||
ARS.harddrop_lock = true
|
||||
|
||||
ARS.spawn_positions = {
|
||||
I = { x=5, y=2 },
|
||||
J = { x=4, y=3 },
|
||||
L = { x=4, y=3 },
|
||||
O = { x=5, y=3 },
|
||||
S = { x=4, y=3 },
|
||||
T = { x=4, y=3 },
|
||||
Z = { x=4, y=3 },
|
||||
}
|
||||
|
||||
ARS.big_spawn_positions = {
|
||||
I = { x=3, y=0 },
|
||||
J = { x=2, y=1 },
|
||||
L = { x=2, y=1 },
|
||||
O = { x=3, y=1 },
|
||||
S = { x=2, y=1 },
|
||||
T = { x=2, y=1 },
|
||||
Z = { x=2, y=1 },
|
||||
}
|
||||
|
||||
function ARS:onPieceCreate(piece, grid)
|
||||
piece.floorkick = 0
|
||||
piece.manipulations = 0
|
||||
|
||||
@@ -6,26 +6,6 @@ local ARS = Ruleset:extend()
|
||||
ARS.name = "ACE-ARS2"
|
||||
ARS.hash = "ArikaACE2"
|
||||
|
||||
ARS.spawn_positions = {
|
||||
I = { x=5, y=2 },
|
||||
J = { x=4, y=3 },
|
||||
L = { x=4, y=3 },
|
||||
O = { x=5, y=3 },
|
||||
S = { x=4, y=3 },
|
||||
T = { x=4, y=3 },
|
||||
Z = { x=4, y=3 },
|
||||
}
|
||||
|
||||
ARS.big_spawn_positions = {
|
||||
I = { x=3, y=0 },
|
||||
J = { x=2, y=1 },
|
||||
L = { x=2, y=1 },
|
||||
O = { x=3, y=1 },
|
||||
S = { x=2, y=1 },
|
||||
T = { x=2, y=1 },
|
||||
Z = { x=2, y=1 },
|
||||
}
|
||||
|
||||
function ARS:onPieceCreate(piece, grid)
|
||||
piece.floorkick = 0
|
||||
piece.manipulations = 0
|
||||
|
||||
@@ -8,26 +8,6 @@ SRS.hash = "ACE Standard"
|
||||
|
||||
SRS.MANIPULATIONS_MAX = 128
|
||||
|
||||
SRS.spawn_positions = {
|
||||
I = { x=5, y=2 },
|
||||
J = { x=4, y=3 },
|
||||
L = { x=4, y=3 },
|
||||
O = { x=5, y=3 },
|
||||
S = { x=4, y=3 },
|
||||
T = { x=4, y=3 },
|
||||
Z = { x=4, y=3 },
|
||||
}
|
||||
|
||||
SRS.big_spawn_positions = {
|
||||
I = { x=3, y=0 },
|
||||
J = { x=2, y=1 },
|
||||
L = { x=2, y=1 },
|
||||
O = { x=3, y=1 },
|
||||
S = { x=2, y=1 },
|
||||
T = { x=2, y=1 },
|
||||
Z = { x=2, y=1 },
|
||||
}
|
||||
|
||||
function SRS:onPieceRotate(piece, grid)
|
||||
piece.lock_delay = 0 -- rotate reset
|
||||
if piece:isDropBlocked(grid) then
|
||||
|
||||
@@ -48,6 +48,27 @@ PAIRS.big_spawn_positions = {
|
||||
[18] = { x=2, y=3 },
|
||||
}
|
||||
|
||||
PAIRS.draw_offsets = {
|
||||
[1] = { x=0, y=0 },
|
||||
[2] = { x=0, y=0 },
|
||||
[3] = { x=0, y=0 },
|
||||
[4] = { x=0, y=0 },
|
||||
[5] = { x=0, y=0 },
|
||||
[6] = { x=0, y=0 },
|
||||
[7] = { x=0, y=0 },
|
||||
[8] = { x=0, y=0 },
|
||||
[9] = { x=0, y=0 },
|
||||
[10] = { x=0, y=0 },
|
||||
[11] = { x=0, y=0 },
|
||||
[12] = { x=0, y=0 },
|
||||
[13] = { x=0, y=0 },
|
||||
[14] = { x=0, y=0 },
|
||||
[15] = { x=0, y=0 },
|
||||
[16] = { x=0, y=0 },
|
||||
[17] = { x=0, y=0 },
|
||||
[18] = { x=0, y=0 },
|
||||
}
|
||||
|
||||
PAIRS.next_sounds = {
|
||||
[1] = "I",
|
||||
[2] = "O",
|
||||
|
||||
@@ -34,6 +34,16 @@ Ruleset.next_sounds = {
|
||||
T = "T"
|
||||
}
|
||||
|
||||
Ruleset.draw_offsets = {
|
||||
I = { x=0, y=0 },
|
||||
J = { x=0, y=0 },
|
||||
L = { x=0, y=0 },
|
||||
O = { x=0, y=0 },
|
||||
S = { x=0, y=0 },
|
||||
T = { x=0, y=0 },
|
||||
Z = { x=0, y=0 },
|
||||
}
|
||||
|
||||
Ruleset.pieces = 7
|
||||
|
||||
-- Component functions.
|
||||
@@ -102,8 +112,14 @@ function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
|
||||
end
|
||||
end
|
||||
|
||||
local was_drop_blocked = piece:isDropBlocked(grid)
|
||||
|
||||
self:attemptRotate(new_inputs, piece, grid, initial)
|
||||
|
||||
if not was_drop_blocked and piece:isDropBlocked(grid) then
|
||||
playSE("bottom")
|
||||
end
|
||||
|
||||
-- prev_inputs becomes the previous inputs
|
||||
for input, value in pairs(inputs) do
|
||||
prev_inputs[input] = inputs[input]
|
||||
@@ -144,17 +160,21 @@ end
|
||||
|
||||
function Ruleset:movePiece(piece, grid, move, instant)
|
||||
local x = piece.position.x
|
||||
local was_drop_blocked = piece:isDropBlocked(grid)
|
||||
if move == "left" then
|
||||
piece:moveInGrid({x=-1, y=0}, 1, grid, false)
|
||||
elseif move == "right" then
|
||||
piece:moveInGrid({x=1, y=0}, 1, grid, false)
|
||||
elseif move == "speedleft" then
|
||||
piece:moveInGrid({x=-1, y=0}, 10, grid, instant)
|
||||
piece:moveInGrid({x=-1, y=0}, grid.width, grid, instant)
|
||||
elseif move == "speedright" then
|
||||
piece:moveInGrid({x=1, y=0}, 10, grid, instant)
|
||||
piece:moveInGrid({x=1, y=0}, grid.width, grid, instant)
|
||||
end
|
||||
if piece.position.x ~= x then
|
||||
self:onPieceMove(piece, grid)
|
||||
if not was_drop_blocked and piece:isDropBlocked(grid) then
|
||||
playSE("bottom")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -162,6 +182,11 @@ function Ruleset:dropPiece(
|
||||
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity
|
||||
)
|
||||
if piece.big then
|
||||
gravity = gravity / 2
|
||||
drop_speed = drop_speed / 2
|
||||
end
|
||||
|
||||
local y = piece.position.y
|
||||
if inputs["down"] == true and drop_locked == false then
|
||||
if additive_gravity then
|
||||
@@ -207,19 +232,33 @@ function Ruleset:initializePiece(
|
||||
end
|
||||
local colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
|
||||
|
||||
local spawn_x
|
||||
if (grid.width ~= 10) then
|
||||
local percent = spawn_positions[data.shape].x / 10
|
||||
for i = 0, grid.width - 1 do
|
||||
if i / grid.width >= percent then
|
||||
spawn_x = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local spawn_dy = (
|
||||
config.gamesettings.spawn_positions == 2 and
|
||||
2 or 0
|
||||
)
|
||||
|
||||
local piece = Piece(data.shape, data.orientation - 1, {
|
||||
x = spawn_positions[data.shape].x,
|
||||
y = spawn_positions[data.shape].y
|
||||
x = spawn_x and spawn_x or spawn_positions[data.shape].x,
|
||||
y = spawn_positions[data.shape].y - spawn_dy
|
||||
}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)
|
||||
|
||||
self:onPieceCreate(piece)
|
||||
if irs then
|
||||
if inputs.rotate_left or inputs.rotate_left2 or
|
||||
inputs.rotate_right or inputs.rotate_right2 or
|
||||
inputs.rotate_180 then
|
||||
self:rotatePiece(inputs, piece, grid, {}, true)
|
||||
if (data.orientation - 1) ~= piece.rotation then
|
||||
playSE("irs")
|
||||
end
|
||||
self:rotatePiece(inputs, piece, grid, {}, true)
|
||||
end
|
||||
self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked)
|
||||
if (buffer_hard_drop and config.gamesettings.buffer_lock == 1) then
|
||||
|
||||
Reference in New Issue
Block a user