diff --git a/package.json b/package.json index 141abf44e..84a89ac05 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "license": "MIT", "dependencies": { "canvas": "^2.8.0", + "cli-table": "^0.3.11", "gif-encoder-2": "^1.0.5", "sha1": "^1.1.1" } diff --git a/utils/rarity.js b/utils/rarity.js index ac577aaf3..3f8133dd4 100644 --- a/utils/rarity.js +++ b/utils/rarity.js @@ -1,82 +1,85 @@ -const basePath = process.cwd(); -const fs = require("fs"); -const layersDir = `${basePath}/layers`; +const Table = require('cli-table'); +const fs = require('fs'); +const { getElements } = require('../src/main.js'); +const { layerConfigurations } = require(`${process.cwd()}/src/config.js`); -const { layerConfigurations } = require(`${basePath}/src/config.js`); +// read metadata +const metadata = JSON.parse( + fs.readFileSync(`${process.cwd()}/build/json/_metadata.json`) +); +const editionSize = metadata.length; -const { getElements } = require("../src/main.js"); +// initialize rarity data +const rarityData = {}; -// read json data -let rawdata = fs.readFileSync(`${basePath}/build/json/_metadata.json`); -let data = JSON.parse(rawdata); -let editionSize = data.length; - -let rarityData = []; +// loop through each layer and trait to collect data +layerConfigurations.forEach((layerConfig) => { + const layers = layerConfig.layersOrder; + layers.forEach((layer) => { + const layerPath = `${process.cwd()}/layers/${layer.name}`; + const elements = getElements(layerPath); + elements.forEach((element) => { + const rarityDataElement = { + trait: element.name, + weight: element.weight.toFixed(2), + occurrence: 0, + }; + if (!rarityData[layer.name]) { + rarityData[layer.name] = []; + } + rarityData[layer.name].push(rarityDataElement); + }); + }); +}); -// intialize layers to chart -layerConfigurations.forEach((config) => { - let layers = config.layersOrder; +// loop through metadata to fill in occurrence data +metadata.forEach((element) => { + element.attributes.forEach((attribute) => { + const layerName = attribute.trait_type; + const value = attribute.value; + const traitData = rarityData[layerName].find((el) => el.trait === value); + traitData.occurrence++; + }); +}); - layers.forEach((layer) => { - // get elements for each layer - let elementsForLayer = []; - let elements = getElements(`${layersDir}/${layer.name}/`); - elements.forEach((element) => { - // just get name and weight for each element - let rarityDataElement = { - trait: element.name, - weight: element.weight.toFixed(0), - occurrence: 0, // initialize at 0 - }; - elementsForLayer.push(rarityDataElement); - }); - let layerName = - layer.options?.["displayName"] != undefined - ? layer.options?.["displayName"] - : layer.name; - // don't include duplicate layers - if (!rarityData.includes(layer.name)) { - // add elements for each layer to chart - rarityData[layerName] = elementsForLayer; - } - }); +// create table header +const table = new Table({ + head: ['Trait type', 'Trait', 'Occurrence'], + colWidths: [20, 20, 40], }); -// fill up rarity chart with occurrences from metadata -data.forEach((element) => { - let attributes = element.attributes; - attributes.forEach((attribute) => { - let traitType = attribute.trait_type; - let value = attribute.value; +// fill table with data +for (const layer in rarityData) { + const layerData = rarityData[layer]; + for (let i = 0; i < layerData.length; i++) { + const traitData = layerData[i]; + const trait = traitData.trait; + const occurrence = `${traitData.occurrence} in ${editionSize} editions (${( + (traitData.occurrence / editionSize) * + 100 + ).toFixed(2)}%)`; + table.push([layer, trait, occurrence]); + } +} - let rarityDataTraits = rarityData[traitType]; - rarityDataTraits.forEach((rarityDataTrait) => { - if (rarityDataTrait.trait == value) { - // keep track of occurrences - rarityDataTrait.occurrence++; - } - }); - }); +// sort table by rarity +table.sort((a, b) => { + const occurrenceA = parseFloat(a[2].split(' ')[0]); + const occurrenceB = parseFloat(b[2].split(' ')[0]); + return occurrenceB - occurrenceA; }); -// convert occurrences to occurence string -for (var layer in rarityData) { - for (var attribute in rarityData[layer]) { - // get chance - let chance = - ((rarityData[layer][attribute].occurrence / editionSize) * 100).toFixed(2); - - // show two decimal places in percent - rarityData[layer][attribute].occurrence = - `${rarityData[layer][attribute].occurrence} in ${editionSize} editions (${chance} %)`; - } -} +// color code the data +table.forEach((row) => { + const occurrence = parseFloat(row[2].split(' ')[0]); + if (occurrence < editionSize * 0.01) { + row[2] = '\x1b[31m' + row[2] + '\x1b[0m'; + } else if (occurrence < editionSize * 0.1) { + row[2] = '\x1b[33m' + row[2] + '\x1b[0m'; + } else { + row[2] = '\x1b[32m' + row[2] + '\x1b[0m'; + } +}); -// print out rarity data -for (var layer in rarityData) { - console.log(`Trait type: ${layer}`); - for (var trait in rarityData[layer]) { - console.log(rarityData[layer][trait]); - } - console.log(); -} +// print table +console.log(table.toString()); diff --git a/yarn.lock b/yarn.lock index 14200dc80..8511d26d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -84,11 +84,23 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +cli-table@^0.3.11: + version "0.3.11" + resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.11.tgz#ac69cdecbe81dccdba4889b9a18b7da312a9d3ee" + integrity sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ== + dependencies: + colors "1.0.3" + color-support@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== +colors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"