From f9b98ef7e410561bed8af408c5adcdc53cb7cbe5 Mon Sep 17 00:00:00 2001 From: ferricles Date: Tue, 20 Dec 2022 15:08:55 -0800 Subject: [PATCH] BitVector fixes w/ unit tests + unit testing page (/unit_tests/) --- js/builder/build_encode_decode.js | 179 ++++++------------------------ js/utils.js | 32 ++---- unit_tests/index.html | 26 +++++ unit_tests/test_main.js | 94 ++++++++++++++++ 4 files changed, 163 insertions(+), 168 deletions(-) create mode 100644 unit_tests/index.html create mode 100644 unit_tests/test_main.js diff --git a/js/builder/build_encode_decode.js b/js/builder/build_encode_decode.js index d67cf64..0823c4e 100644 --- a/js/builder/build_encode_decode.js +++ b/js/builder/build_encode_decode.js @@ -4,6 +4,37 @@ let build_powders; function getItemNameFromID(id) { return idMap.get(id); } function getTomeNameFromID(id) { return tomeIDMap.get(id); } +function parsePowdering(powder_info) { + // TODO: Make this run in linear instead of quadratic time... ew + let powdering = []; + for (let i = 0; i < 5; ++i) { + let powders = ""; + let n_blocks = Base64.toInt(powder_info.charAt(0)); + // console.log(n_blocks + " blocks"); + powder_info = powder_info.slice(1); + for (let j = 0; j < n_blocks; ++j) { + let block = powder_info.slice(0,5); + let six_powders = Base64.toInt(block); + for (let k = 0; k < 6 && six_powders != 0; ++k) { + powders += powderNames.get((six_powders & 0x1f) - 1); + six_powders >>>= 5; + } + powder_info = powder_info.slice(5); + } + powdering[i] = powders; + } + return [powdering, powder_info]; +} + +let atree_data = null; +const wynn_version_names = [ + '2.0.1.1', + '2.0.1.2' +]; +const WYNN_VERSION_LATEST = wynn_version_names.length - 1; +// Default to the newest version. +let wynn_version_id = WYNN_VERSION_LATEST; + /* * Populate fields based on url, and calculate build. * TODO: THIS CODE IS GOD AWFUL result of being lazy @@ -193,154 +224,6 @@ async function parse_hash(url_tag) { } } -function parsePowdering(powder_info) { - // TODO: Make this run in linear instead of quadratic time... ew - let powdering = []; - for (let i = 0; i < 5; ++i) { - let powders = ""; - let n_blocks = Base64.toInt(powder_info.charAt(0)); - // console.log(n_blocks + " blocks"); - powder_info = powder_info.slice(1); - for (let j = 0; j < n_blocks; ++j) { - let block = powder_info.slice(0,5); - let six_powders = Base64.toInt(block); - for (let k = 0; k < 6 && six_powders != 0; ++k) { - powders += powderNames.get((six_powders & 0x1f) - 1); - six_powders >>>= 5; - } - powder_info = powder_info.slice(5); - } - powdering[i] = powders; - } - return [powdering, powder_info]; -} - -let atree_data = null; - -/* - * Populate fields based on url, and calculate build. - */ -function decodeBuild(url_tag) { - if (url_tag) { - //default values - let equipment = [null, null, null, null, null, null, null, null, null]; - let tomes = [null, null, null, null, null, null, null]; - let powdering = ["", "", "", "", ""]; - let info = url_tag.split("_"); - let version = info[0]; - let save_skp = false; - let skillpoints = [0, 0, 0, 0, 0]; - let level = 106; - - let version_number = parseInt(version) - //equipment (items) - // TODO: use filters - if (version_number < 4) { - let equipments = info[1]; - for (let i = 0; i < 9; ++i ) { - let equipment_str = equipments.slice(i*3,i*3+3); - equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); - } - info[1] = equipments.slice(27); - } - else if (version_number == 4) { - let info_str = info[1]; - let start_idx = 0; - for (let i = 0; i < 9; ++i ) { - if (info_str.charAt(start_idx) === "-") { - equipment[i] = "CR-"+info_str.slice(start_idx+1, start_idx+18); - start_idx += 18; - } - else { - let equipment_str = info_str.slice(start_idx, start_idx+3); - equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); - start_idx += 3; - } - } - info[1] = info_str.slice(start_idx); - } - else if (version_number <= 7) { - let info_str = info[1]; - let start_idx = 0; - for (let i = 0; i < 9; ++i ) { - if (info_str.slice(start_idx,start_idx+3) === "CR-") { - equipment[i] = info_str.slice(start_idx, start_idx+20); - start_idx += 20; - } else if (info_str.slice(start_idx+3,start_idx+6) === "CI-") { - let len = Base64.toInt(info_str.slice(start_idx,start_idx+3)); - equipment[i] = info_str.slice(start_idx+3,start_idx+3+len); - start_idx += (3+len); - } else { - let equipment_str = info_str.slice(start_idx, start_idx+3); - equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); - start_idx += 3; - } - } - info[1] = info_str.slice(start_idx); - } - //constant in all versions - for (let i in equipment) { - setValue(equipment_inputs[i], equipment[i]); - } - - //level, skill point assignments, and powdering - if (version_number == 1) { - let powder_info = info[1]; - let res = parsePowdering(powder_info); - powdering = res[0]; - } else if (version_number == 2) { - save_skp = true; - let skillpoint_info = info[1].slice(0, 10); - for (let i = 0; i < 5; ++i ) { - skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2)); - } - - let powder_info = info[1].slice(10); - let res = parsePowdering(powder_info); - powdering = res[0]; - } else if (version_number <= 7){ - level = Base64.toInt(info[1].slice(10,12)); - setValue("level-choice",level); - save_skp = true; - let skillpoint_info = info[1].slice(0, 10); - for (let i = 0; i < 5; ++i ) { - skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2)); - } - - let powder_info = info[1].slice(12); - - let res = parsePowdering(powder_info); - powdering = res[0]; - info[1] = res[1]; - } - // Tomes. - if (version >= 6 && version < 8) { - //tome values do not appear in anything before v6. - for (let i in tomes) { - let tome_str = info[1].charAt(i); - let tome_name = getTomeNameFromID(Base64.toInt(tome_str)); - setValue(tomeInputs[i], tome_name); - } - info[1] = info[1].slice(7); - } - - if (version == 7) { - // ugly af. only works since its the last thing. will be fixed with binary decode - atree_data = new BitVector(info[1]); - } - else { - atree_data = null; - } - - for (let i in powder_inputs) { - setValue(powder_inputs[i], powdering[i]); - } - for (let i in skillpoints) { - setValue(skp_order[i] + "-skp", skillpoints[i]); - } - } -} - /* Stores the entire build in a string using B64 encoding and adds it to the URL. */ function encodeBuild(build, powders, skillpoints, atree, atree_state) { diff --git a/js/utils.js b/js/utils.js index c432812..d3211cf 100644 --- a/js/utils.js +++ b/js/utils.js @@ -176,7 +176,7 @@ Base64 = (function () { if (data == 0) { length = 0; } else { - length = Math.ceil(Math.log(data) / Math.log(2)); + length = Math.ceil(Math.log(data + 1) / Math.log(2)); //+1 to account for powers of 2 } } if (length < 0) { @@ -211,7 +211,7 @@ Base64 = (function () { */ read_bit(idx) { if (idx < 0 || idx >= this.length) { - throw new RangeError("Cannot read bit outside the range of the BitVector. ("+idx+" > "+this.length+")"); + throw new RangeError("Cannot read bit outside the range of the BitVector. ("+idx+" >= "+this.length+")"); } return ((this.bits[Math.floor(idx / 32)] & (1 << idx)) == 0 ? 0 : 1); } @@ -369,7 +369,7 @@ Base64 = (function () { } } else if (typeof data === "number") { //convert to int just in case - let int = Math.round(data); + let int = Math.round(data); //range of numbers that "could" fit in a uint32 -> [0, 2^32) U [-2^31, 2^31) if (data > 2**32 - 1 || data < -(2 ** 31)) { @@ -377,13 +377,19 @@ Base64 = (function () { } //could be split between multiple new ints //reminder that shifts implicitly mod 32 - this.bits[curr_idx] |= ((int & ~((~0) << length)) << (this.length)); - if (((this.length - 1) % 32 + 1) + length > 32) { + if (length == 32) { + this.bits[curr_idx] |= int << (this.length); + } else { + this.bits[curr_idx] |= ((int & ~((~0) << length)) << (this.length)); + } + + //overflow part + if ((pos % 32) + length > 32) { this.bits[curr_idx + 1] = (int >>> (32 - this.length)); } } else if (data instanceof BitVector) { //fill to end of curr int of existing bv - let other_pos = (32 - (pos % 32)) % 32; + let other_pos = (32 - (pos % 32)); this.bits[curr_idx] |= data.slice(0, other_pos); curr_idx += 1; @@ -405,18 +411,6 @@ Base64 = (function () { } }; -function test_bv() { - //empty array - let bv = new BitVector(0); - bv.append(10, 4); - console.log(bv); - - bv = new BitVector(0); - bv.append(10, 5); - console.log(bv); - -} - /* Turns a raw stat and a % stat into a final stat on the basis that - raw and >= 100% becomes 0 and + raw and <=-100% becomes negative. @@ -1054,5 +1048,3 @@ if (screen.width < 992) { scrollPos = document.documentElement.scrollTop; }); } - -test_bv(); \ No newline at end of file diff --git a/unit_tests/index.html b/unit_tests/index.html new file mode 100644 index 0000000..7298b7d --- /dev/null +++ b/unit_tests/index.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + WynnBuilder + +
+ Scram kid this page ain't for you +
+ + + + + + \ No newline at end of file diff --git a/unit_tests/test_main.js b/unit_tests/test_main.js new file mode 100644 index 0000000..60f38e7 --- /dev/null +++ b/unit_tests/test_main.js @@ -0,0 +1,94 @@ + + +//requires js/utils.js +function test_bv() { + /* BASIC TESTS - NO EDGE CASES */ + //empty array + let bv = new BitVector(0); + console.log(bv.toB64()); //0 + bv.append(10, 4); + console.log(bv.toB64()); //A + bv.append(10, 4); + console.log(bv.toB64(), bv.bits); //g2, 170 (0b10 101010) + + bv = new BitVector(""); + console.log(bv.toB64(), bv.bits, bv.length); + bv.append("A"); + console.log(bv.toB64(), bv.bits); //A + bv.append("102"); + console.log(bv.toB64(), bv.bits); //A102 + + + + //make sure extra length doesn't do anything + bv = new BitVector(0); + bv.append(10, 5); + console.log(bv.toB64()); //A + bv.append(10, 4); + console.log(bv.toB64(), bv.bits); //a5, 330 (0b101 001010) + + //non-empty array + bv = new BitVector(32); //100000 + console.log(bv.toB64(), bv.bits); //W, 32 (0b 100000) + bv.append(1, 1); + console.log(bv.toB64(), bv.bits); //W1, 96 (0b 1 100000) + bv.append(10, 4); + console.log(bv.toB64(), bv.bits); //WL, 1376 (0b 10101 100000) + + bv = new BitVector(7, 2); + bv.append("ABCDE"); + console.log(bv.toB64(), bv.bits, bv.length); // limqu0, 0b 00 111000 110100 110000 101100 101111 + + //bit ops + console.log(bv.read_bit(7)); //0 + bv.set_bit(7); + console.log(bv.read_bit(7)); //1 + console.log(bv.toB64(), bv.bits); //WN , 1504 (0b 10111 100000) + bv.clear_bit(7); + console.log(bv.read_bit(7)); //0 + + + /* SIMPLE EDGE CASE TESTS*/ + + // string -> 3 ints + bv = new BitVector("a1s2d3f4A9XJKw-m"); + console.log(bv.toB64(), bv.bits, bv.length); + bv.append("M+LKeoxZJ0JELW0x"); + console.log(bv.toB64(), bv.bits, bv.length); + + + //full int + bv = new BitVector(4294967295); + console.log(bv.toB64(), bv.bits, bv.length); //-----3, 4294967295, (0b 11 111111 111111 111111 111111 111111) + + //append single bit to full int + bv.append(1, 1); + console.log(bv.toB64(), bv.bits, bv.length); //-----7, [4294967295, 1], (0b 111 1s...) + + + //append full int to full int to full int + bv = new BitVector(4294967295); + bv.append(4294967280); + console.log(bv.toB64(), bv.bits, bv.length); // -----3----F, [4294967295, 4294967280], (0b 1111 1....1 000011 111111 ...) + bv.append(4294967167); + console.log(bv.toB64(), bv.bits, bv.length); // -----3-----V----, [4294967295, 4294967280, 4294967167, 0] (0b 1...1 011111 1....1 000011 111111) + + + /* BIG TESTS */ + bv = new BitVector(12341234); //(0b 101111 000100 111111 110010) + console.log(bv.toB64(), bv.bits, bv.length); // o-4l, 12341234 (0b 101111 000100 111111 110010) + console.log(bv.slice(10, 15)); //19 (100 11) + bv.append(4113241323); //(0b 11 110101 001010 110001 010011 101011) + bv.append(2213461274); //(0b 1000 001111 101110 101111 010001 1010) + bv.append(1273491384); //(0b 10010 111110 011111 101111 101110 00) + bv.append(1828394744); //(0b 110110 011111 011000 101101 111100 0) + bv.append(1938417329); //(0b 1 110011 100010 011110 011010 110001) + console.log(bv.toB64(), bv.bits, bv.length); //o -4lhJn ArhHlk F8klV+ IuRn+i 5hv9E7 + + bv = new BitVector(""); + bv_2 = new BitVector("a1s2d3f4A9XJKw-m"); + console.log(bv_2.toB64(), bv_2.bits, bv_2.length); + bv.append(bv_2); + console.log(bv.toB64(), bv.bits, bv.length); + +} \ No newline at end of file