inital
This commit is contained in:
parent
f424be4a1d
commit
a579b5334e
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,6 +3,9 @@ __pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
recipes.json
|
||||
backup/
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
|
||||
19
README.md
19
README.md
@ -1,2 +1,21 @@
|
||||
# LuckyColorRecipeCalculator
|
||||
A luckycolor recipe calculator for islet to get maximum protection.
|
||||
|
||||
# What is this?
|
||||
|
||||
Islet is a minecraft server that have been playing lately and it has a special armor system. In short, only leather armor could reduce the damage and the degree of the reduction depends on the manhattan distance between the color of the leather armor and a randomly generated color named "luckycolor".
|
||||
|
||||
To this end, inspired by [ANRAR4](https://github.com/ANRAR4)'s repo [ANRAR4/DyeLeatherArmor](https://github.com/ANRAR4/DyeLeatherArmor), I re-write the code that calculate the craftable colors in c++ and wrote a python script that helps to find the closest color to "luckycolor".
|
||||
|
||||
# How to use?
|
||||
|
||||
## I just want to calculat my luckycolor:
|
||||
1. decompress the file ```./data/recipes.7z```, you will get ```recipes.json```. Put it in the current floder.
|
||||
2. run ```python find_recipe.py```
|
||||
|
||||
## How can I get recipe.json from scratch:
|
||||
1. compile ```leatherColorCombinationsCalculator.cpp``` with ```g++ -O3 -Wall leatherColorCombinationsCalculator.cpp -lpthread -o leatherColorCombinationsCalculator```
|
||||
|
||||
2. run ```./leatherColorCombinationsCalculator <numThreads>```, it will produce ```layer1-14.bin``` (the generated file is also provided in ./data/layers.7z)
|
||||
|
||||
3. run ```python generate_recipes.py``` to generate ```recipes.json```.
|
||||
|
||||
106
colors.py
Normal file
106
colors.py
Normal file
@ -0,0 +1,106 @@
|
||||
from base64 import decode, encode
|
||||
from torch import full
|
||||
|
||||
|
||||
mc_colors = [
|
||||
['红色染料', '#B02E26', 'red'],
|
||||
['绿色染料', '#5E7C16', 'green'],
|
||||
['紫色染料', '#8932B8', 'purple'],
|
||||
['青色染料', '#169C9C', 'cyan'],
|
||||
['淡灰色染料', '#9D9D97', 'silver'],
|
||||
['灰色染料', '#474F52', 'gray'],
|
||||
['粉红色染料', '#F38BAA', 'pink'],
|
||||
['黄绿色染料', '#80C71F', 'lime'],
|
||||
['黄色染料', '#FED83D', 'yellow'],
|
||||
['淡蓝色染料', '#3AB3DA', 'lightBlue'],
|
||||
['品红色染料', '#C74EBD', 'magenta'],
|
||||
['橙色染料', '#F9801D', 'orange'],
|
||||
['黑色染料', '#1D1D21', 'black'],
|
||||
['棕色染料', '#835432', 'brown'],
|
||||
['蓝色染料', '#3C44AA', 'blue'],
|
||||
['白色染料', '#F9FFFE', 'white']
|
||||
]
|
||||
|
||||
colornames = [
|
||||
"red", "green", "purple", "cyan",
|
||||
"silver", "gray", "pink", "lime",
|
||||
"yellow", "lightBlue", "magenta", "orange",
|
||||
"black", "brown", "blue", "white"
|
||||
]
|
||||
|
||||
def encode_color(r, g, b):
|
||||
color = '#{r}{g}{b}'.format(
|
||||
r = hex(r)[2:].rjust(2, '0'),
|
||||
g = hex(g)[2:].rjust(2, '0'),
|
||||
b = hex(b)[2:].rjust(2, '0'),
|
||||
)
|
||||
return color
|
||||
|
||||
def decode_color(color:str):
|
||||
color = color.strip('#')
|
||||
color = int(color, base=16)
|
||||
r = (color>>16) & 0xff
|
||||
g = (color>>8) & 0xff
|
||||
b = color & 0xff
|
||||
return [r, g, b]
|
||||
|
||||
def parse_mc_color(colors):
|
||||
parsed_color = {}
|
||||
for color in colors:
|
||||
name, value, key = color
|
||||
assert len(value) == 7
|
||||
r, g, b = decode_color(value)
|
||||
parsed_color[key] = {
|
||||
"name": name,
|
||||
"key": key,
|
||||
"value": [r, g, b]
|
||||
}
|
||||
return parsed_color
|
||||
|
||||
colors = parse_mc_color(mc_colors)
|
||||
|
||||
def calculate_fused_color(colors):
|
||||
if len(colors) == 0:
|
||||
return None
|
||||
# calculate average color
|
||||
avg_color = []
|
||||
num_colors = len(colors)
|
||||
for channel in zip(*colors):
|
||||
channel_avg = sum(channel) // num_colors
|
||||
avg_color.append(channel_avg)
|
||||
# calculate gain factor.
|
||||
avg_max = sum([max(x) for x in colors])/num_colors
|
||||
max_avg = max(avg_color)
|
||||
# print('avgcolor:', avg_color)
|
||||
# print('avgmax:', avg_max)
|
||||
# print('maxavg:', max_avg)
|
||||
gain_factor = avg_max / max_avg
|
||||
fused_color = [int(x*gain_factor) for x in avg_color]
|
||||
return fused_color
|
||||
|
||||
def get_color_by_recipe(recipe, current_color=None):
|
||||
color_list = []
|
||||
for key, count in recipe.items():
|
||||
if count > 0:
|
||||
color_list += [colors[key]['value']]*count
|
||||
if current_color is not None:
|
||||
color_list.append(current_color)
|
||||
return color_list
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# color = get_color_by_recipe({
|
||||
# "purple": 1,
|
||||
# "blue": 1,
|
||||
# "white": 6
|
||||
# })
|
||||
# print('color:', color)
|
||||
# print(calculate_fused_color(color))
|
||||
|
||||
color = [123, 59, 71]
|
||||
encoded = encode_color(*color)
|
||||
decoded = decode_color(encoded)
|
||||
print('color:', color)
|
||||
print('encoded:', encoded)
|
||||
print('decoded:', decoded)
|
||||
BIN
data/layers.7z
Normal file
BIN
data/layers.7z
Normal file
Binary file not shown.
BIN
data/recipes.7z
Normal file
BIN
data/recipes.7z
Normal file
Binary file not shown.
156
find_recipe.py
Normal file
156
find_recipe.py
Normal file
@ -0,0 +1,156 @@
|
||||
from base64 import decode
|
||||
from utils import load_dict
|
||||
from colors import colors, colornames, decode_color
|
||||
import math
|
||||
|
||||
def calculate_leather_color(recipes):
|
||||
cache = {}
|
||||
for layer in recipes:
|
||||
for color in recipes[layer]:
|
||||
cache[color] = {
|
||||
"color": decode_color(color),
|
||||
"step": layer
|
||||
}
|
||||
return cache
|
||||
|
||||
def color_distance(c1, c2):
|
||||
return sum([abs(x - y) for x, y in zip(c1, c2)])
|
||||
|
||||
def find_closest_color(cache, color, max_step=-1):
|
||||
min_dist = 10000
|
||||
closest_color = None
|
||||
for name, this_color in cache.items():
|
||||
if max_step > 0 and this_color['step'] > max_step:
|
||||
continue
|
||||
this_dist = color_distance(color, this_color['color'])
|
||||
if this_dist < min_dist:
|
||||
min_dist = this_dist
|
||||
closest_color = name
|
||||
return closest_color, min_dist
|
||||
|
||||
def decode_recipe(encoded):
|
||||
encoded = int(encoded, base=16)
|
||||
counts = []
|
||||
while len(counts) < 16:
|
||||
counts.append(encoded%9)
|
||||
encoded //=9
|
||||
return {name:count for name, count in zip(colornames, counts) if count > 0}
|
||||
|
||||
def query_recipe(recipes, name):
|
||||
# search color layer.
|
||||
recipe = None
|
||||
for layer in recipes:
|
||||
recipe = recipes[layer].get(name, None)
|
||||
if recipe is not None:
|
||||
break
|
||||
if recipe is None:
|
||||
raise ValueError('Color not craftable.')
|
||||
base_color, recipe = recipe
|
||||
recipe = decode_recipe(recipe)
|
||||
if base_color == '#000000':
|
||||
return [recipe]
|
||||
else:
|
||||
steps = query_recipe(recipes, base_color)
|
||||
steps.append(recipe)
|
||||
return steps
|
||||
|
||||
def explain_recipe(steps):
|
||||
infos = []
|
||||
for i, step in enumerate(steps):
|
||||
info = ','.join(['{0}*{1}'.format(colors[name]['name'], count) for name, count in step.items()])
|
||||
infos.append(info)
|
||||
print('->'.join(infos))
|
||||
|
||||
def calculate_multipler(diff, enchant=False):
|
||||
diff = 256 if diff > 256 else diff
|
||||
multiplier = None
|
||||
if enchant:
|
||||
multiplier = 1 - (0.50) * (256.0 - diff)*0.5 / 256.0
|
||||
else:
|
||||
multiplier = 1 - (0.50) * (256.0 - diff) / 256.0
|
||||
multiplier = multiplier * math.log(80) / math.log(125) # single cloth.
|
||||
return multiplier
|
||||
|
||||
|
||||
def show_protection(diff):
|
||||
print('============= 减伤系数 =============')
|
||||
multiplier = calculate_multipler(diff, enchant=False)
|
||||
multiplier_full = multiplier**4
|
||||
multiplier_elytra = multiplier**3 * math.log(432) / math.log(125)
|
||||
info = '\n'.join([
|
||||
"无附魔情况下,单件衣服减伤系数为:{0}".format(multiplier),
|
||||
"无附魔情况下,全套衣服减伤系数为:{0}".format(multiplier_full),
|
||||
"无附魔情况下,三件衣服+鞘翅减伤系数为:{0}".format(multiplier_elytra),
|
||||
])
|
||||
print(info)
|
||||
multiplier = calculate_multipler(diff, enchant=True)
|
||||
multiplier_full = multiplier**4
|
||||
multiplier_elytra = multiplier**3 * math.log(432) / math.log(125)
|
||||
info = '\n'.join([
|
||||
"有附魔情况下,单件衣服减伤系数为:{0}".format(multiplier),
|
||||
"有附魔情况下,全套衣服减伤系数为:{0}".format(multiplier_full),
|
||||
"有附魔情况下,三件衣服+鞘翅减伤系数为:{0}".format(multiplier_elytra),
|
||||
])
|
||||
print(info)
|
||||
print('在有保护类附魔存在时,实际减伤表格如下所示。其中横排分别表示单件、全套、有鞘翅三种情况,纵列分别表示保护系数为0/1/2/3/4/5')
|
||||
print('(保护系数的计算:上限为5,其中保护四记为1,火焰、爆炸、弹射物保护对其对应的伤害时记为2,摔落保护对摔落保护记为3,每件衣服的保护系数可以直接相加且上限为5,超过5记为5)')
|
||||
multiplier_protection = [1 - x*4/25 for x in range(6)]
|
||||
multiplier_protect_enchant = [multiplier*this_protection for this_protection in multiplier_protection]
|
||||
multiplier_full_protect_enchant = [multiplier_full*this_protection for this_protection in multiplier_protection]
|
||||
multiplier_elytra_protect_enchant = [multiplier_elytra*this_protection for this_protection in multiplier_protection]
|
||||
info = '\n'.join([
|
||||
' '.join(['%.4f']*6)%tuple(multiplier_protect_enchant),
|
||||
' '.join(['%.4f']*6)%tuple(multiplier_full_protect_enchant),
|
||||
' '.join(['%.4f']*6)%tuple(multiplier_elytra_protect_enchant)
|
||||
])
|
||||
print(info)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('正在加载配方数据...')
|
||||
recipes = load_dict('./recipes.json')
|
||||
print('配方数据库加载完成!正在准备数据...')
|
||||
leather_color_cache = calculate_leather_color(recipes)
|
||||
print('数据准备完毕,共加载{0}种可合成颜色。'.format(len(leather_color_cache)))
|
||||
|
||||
note = '\n'.join(
|
||||
[
|
||||
'请输入你的幸运色,用空格分隔。',
|
||||
'如你的幸运色为100, 200, 100, 请输入 100 200 100。',
|
||||
'默认所有颜色可用,若你希望限制合成的步骤数量,可以在幸运色后追加一个数字表示每种颜色的最大合成步骤',
|
||||
'如 100 200 100 2 表示给出的配方中最多进行两次合成。'
|
||||
])
|
||||
print(note)
|
||||
|
||||
while True:
|
||||
luckycolor = None
|
||||
max_step = -1
|
||||
while luckycolor is None:
|
||||
try:
|
||||
luckycolor_str = input('>>> ')
|
||||
luckycolor = luckycolor_str.split()
|
||||
luckycolor = [int(x) for x in luckycolor]
|
||||
if len(luckycolor) < 3 or len(luckycolor) > 4:
|
||||
raise ValueError
|
||||
if len(luckycolor) == 4:
|
||||
max_step = luckycolor[3]
|
||||
luckycolor = luckycolor[:3]
|
||||
except Exception:
|
||||
print('幸运色颜色格式错误qwq')
|
||||
luckycolor = None
|
||||
|
||||
print('正在寻找最接近的可合成颜色...')
|
||||
name, dist = find_closest_color(leather_color_cache, luckycolor, max_step=max_step)
|
||||
color = decode_color(name)
|
||||
print('最接近的可合成颜色为: {0}{1},曼哈顿距离误差为:{2}'.format(
|
||||
name, tuple(color), dist
|
||||
))
|
||||
|
||||
show_protection(dist)
|
||||
|
||||
print('查找配方中...')
|
||||
crafting_steps = query_recipe(recipes, name)
|
||||
print('合成配方如下:')
|
||||
explain_recipe(crafting_steps)
|
||||
|
||||
|
||||
65
generate_recipes.py
Normal file
65
generate_recipes.py
Normal file
@ -0,0 +1,65 @@
|
||||
import os
|
||||
import struct
|
||||
from utils import save_dict
|
||||
from colors import encode_color, colornames, colors
|
||||
|
||||
|
||||
def decode_color_from_bytes(buffer):
|
||||
color = struct.unpack('L', buffer)[0]
|
||||
r = (color>>16) & 0xff
|
||||
g = (color>>8) & 0xff
|
||||
b = color & 0xff
|
||||
return r, g, b
|
||||
|
||||
def decode_recipe_from_bytes(buffer):
|
||||
encoded = struct.unpack('Q', buffer)[0]
|
||||
# recipe = []
|
||||
# while len(recipe) < 16:
|
||||
# recipe.append(encoded%9)
|
||||
# encoded //=9
|
||||
# return {name:count for name, count in zip(colornames, recipe) if count > 0}
|
||||
return hex(encoded)[2:].rjust(16, '0')
|
||||
|
||||
def load_recipe(filepath):
|
||||
file = open(filepath, 'rb')
|
||||
while True:
|
||||
base_color = file.read(4)
|
||||
if base_color == b'':
|
||||
break
|
||||
recipe = file.read(8)
|
||||
if recipe == b'':
|
||||
raise ValueError
|
||||
crafted_color = file.read(4)
|
||||
|
||||
base_color = decode_color_from_bytes(base_color)
|
||||
recipe = decode_recipe_from_bytes(recipe)
|
||||
crafted_color = decode_color_from_bytes(crafted_color)
|
||||
yield base_color, recipe, crafted_color
|
||||
|
||||
def convert_recipes(fmt='./layer{0}.bin', save_path='./recipes.json'):
|
||||
i = 1
|
||||
print('checking', fmt.format(i))
|
||||
recipe_db = {}
|
||||
counter = 0
|
||||
while os.path.exists(fmt.format(i)):
|
||||
path = fmt.format(i)
|
||||
print('loading ', path)
|
||||
recipes = load_recipe(path)
|
||||
this_converted_recipe = {}
|
||||
this_counter = 0
|
||||
for base_color, recipe, crafted_color in recipes:
|
||||
crafted_color = encode_color(*crafted_color)
|
||||
base_color = encode_color(*base_color)
|
||||
this_converted_recipe[crafted_color] = [base_color, recipe]
|
||||
this_counter += 1
|
||||
recipe_db[str(i)] = this_converted_recipe
|
||||
print('layer {0} has {1} colors.'.format(i, this_counter))
|
||||
counter += this_counter
|
||||
i += 1
|
||||
print('{0} colors loaded in total.'.format(counter))
|
||||
print('writing result to', save_path)
|
||||
save_dict(save_path, recipe_db, indent=None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
convert_recipes()
|
||||
404
leatherColorCombinationsCalculator.cpp
Normal file
404
leatherColorCombinationsCalculator.cpp
Normal file
@ -0,0 +1,404 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
template<typename T>
|
||||
void printVector(std::vector<T> vec){
|
||||
for (size_t i = 0; i < vec.size(); i++)
|
||||
{
|
||||
std::cout<<vec[i];
|
||||
if (i < vec.size() - 1) {
|
||||
std::cout<<", ";
|
||||
}
|
||||
}
|
||||
std::cout<<std::endl;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void printMatrix(std::vector<std::vector<T>> mat){
|
||||
for (size_t i = 0; i < mat.size(); i++)
|
||||
{
|
||||
printVector(mat[i]);
|
||||
}
|
||||
}
|
||||
|
||||
class Color
|
||||
{
|
||||
public:
|
||||
Color():r(0), g(0), b(0){}
|
||||
Color(const Color& color) {
|
||||
r = color.r;
|
||||
g = color.g;
|
||||
b = color.b;
|
||||
}
|
||||
Color(uint32_t color){
|
||||
Color c = Color::decode(color);
|
||||
r = c.r;
|
||||
g = c.g;
|
||||
b = c.b;
|
||||
}
|
||||
Color(int _r, int _g, int _b){
|
||||
r = uint8_t(_r);
|
||||
g = uint8_t(_g);
|
||||
b = uint8_t(_b);
|
||||
}
|
||||
|
||||
uint32_t encode(void) const{
|
||||
return (uint32_t(r)<<16) + (uint32_t(g)<<8) + uint32_t(b);
|
||||
}
|
||||
static Color decode(uint32_t val) {
|
||||
uint8_t r = (val>>16) & 0xff;
|
||||
uint8_t g = (val>>8) & 0xff;
|
||||
uint8_t b = val & 0xff;
|
||||
return Color(r, g, b);
|
||||
}
|
||||
void show(void) {
|
||||
std::cout<<"(r, g, b) = ("<<uint32_t(r)<<", "<<uint32_t(g)<<", "<<uint32_t(b)<<")"<<std::endl;
|
||||
}
|
||||
|
||||
bool operator==(const Color& color) const {
|
||||
return (r == color.r) && (g == color.g) && (b ==color.b);
|
||||
}
|
||||
|
||||
public:
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
class ColorCraftingTable {
|
||||
public:
|
||||
ColorCraftingTable(void){
|
||||
totalRed = 0;
|
||||
totalGreen = 0;
|
||||
totalBlue = 0;
|
||||
maxSum = 0;
|
||||
numColors = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
void addColor(const Color& color) {
|
||||
totalRed += color.r;
|
||||
totalGreen += color.g;
|
||||
totalBlue += color.b;
|
||||
maxSum += std::max(color.r, std::max(color.g, color.b));
|
||||
numColors += 1;
|
||||
}
|
||||
|
||||
Color craft() {
|
||||
uint32_t avgRed = totalRed / numColors;
|
||||
uint32_t avgGreen = totalGreen / numColors;
|
||||
uint32_t avgBlue = totalBlue / numColors;
|
||||
float avgOfMax = maxSum*1.0 / numColors;
|
||||
uint32_t maxAvg = std::max(avgRed, std::max(avgGreen, avgBlue));
|
||||
// std::cout<<numColors<<","<<avgRed<<","<<avgGreen<<","<<avgBlue<<","<<avgOfMax<<","<<maxAvg<<","<<std::endl;
|
||||
return Color(
|
||||
uint8_t(float(avgRed)*avgOfMax/maxAvg),
|
||||
uint8_t(float(avgGreen)*avgOfMax/maxAvg),
|
||||
uint8_t(float(avgBlue)*avgOfMax/maxAvg)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t totalRed;
|
||||
uint32_t totalGreen;
|
||||
uint32_t totalBlue;
|
||||
uint32_t maxSum;
|
||||
uint32_t numColors;
|
||||
};
|
||||
|
||||
void generateCompose(int total, int choice, std::vector<int>& current, std::vector<std::vector<int>>& ans){
|
||||
if (choice <= 0) {
|
||||
std::vector<int> compose;
|
||||
for (size_t i = 0; i < current.size(); i++)
|
||||
{
|
||||
compose.push_back(current[current.size() - 1 - i]);
|
||||
}
|
||||
ans.push_back(compose);
|
||||
return;
|
||||
}
|
||||
if (total<=0 || choice <=0 || choice > total) return;
|
||||
// do not choose current number
|
||||
generateCompose(total-1, choice, current, ans);
|
||||
// choose current number.
|
||||
current.push_back(total);
|
||||
generateCompose(total-1, choice-1, current, ans);
|
||||
current.pop_back();
|
||||
}
|
||||
|
||||
std::vector<int> composeToRecipe(const std::vector<int>& compose, int num) {
|
||||
std::vector<int> pre = compose;
|
||||
std::vector<int> post = compose;
|
||||
pre.insert(pre.begin(), 0);
|
||||
post.push_back(num);
|
||||
std::vector<int> recipe;
|
||||
for (size_t i = 1; i < pre.size(); i++)
|
||||
{
|
||||
recipe.push_back(post[i] - pre[i] - 1);
|
||||
}
|
||||
return recipe;
|
||||
}
|
||||
|
||||
class Recipe {
|
||||
public:
|
||||
Recipe(const std::vector<int>& _counts){
|
||||
counts = _counts;
|
||||
}
|
||||
|
||||
public:
|
||||
uint64_t encode(void) {
|
||||
uint64_t val = 0;
|
||||
uint64_t bits = 1;
|
||||
for (size_t i = 0; i < counts.size(); i++)
|
||||
{
|
||||
val += bits * counts[i];
|
||||
bits *= 9;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
static Recipe decode(uint64_t encoded) {
|
||||
std::vector<int> counts;
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
{
|
||||
counts.push_back(int(encoded%9));
|
||||
encoded /= 9;
|
||||
}
|
||||
return Recipe(counts);
|
||||
}
|
||||
void preCraft(const std::vector<Color>& colorlist) {
|
||||
for (size_t i = 0; i < counts.size(); i++)
|
||||
{
|
||||
for (int j = 0; j < counts[i]; j++)
|
||||
{
|
||||
table.addColor(colorlist[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Color getTableColor(void) {
|
||||
return table.craft();
|
||||
}
|
||||
|
||||
Color getCraftedArmorColor(const Color armorColor) {
|
||||
ColorCraftingTable temp = table;
|
||||
if (!(armorColor == Color(0, 0, 0))) {
|
||||
temp.addColor(armorColor);
|
||||
}
|
||||
return temp.craft();
|
||||
}
|
||||
|
||||
std::vector<int> getCounts(void){
|
||||
return counts;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int> counts;
|
||||
ColorCraftingTable table;
|
||||
|
||||
};
|
||||
|
||||
void dumpPossibleRecipe(const std::string& path) {
|
||||
std::vector<std::vector<int>> ans;
|
||||
std::vector<int> current;
|
||||
std::fstream recipes;
|
||||
recipes.open(path, std::fstream::out|std::fstream::binary);
|
||||
generateCompose(24, 16, current, ans);
|
||||
size_t counts = 0;
|
||||
for (size_t i = 0; i < ans.size(); i++)
|
||||
{
|
||||
Recipe recipe(composeToRecipe(ans[i], 25));
|
||||
uint64_t encoded = recipe.encode();
|
||||
if (encoded == 0) continue; // this recipe is empty.
|
||||
recipes.write((char*) &encoded, sizeof(encoded));
|
||||
counts += 1;
|
||||
}
|
||||
recipes.close();
|
||||
std::cout<<"we have write "<<counts<<" possible recipes."<<std::endl;
|
||||
}
|
||||
|
||||
template<int N>
|
||||
class SyncholizedHeapBitset {
|
||||
public:
|
||||
SyncholizedHeapBitset(void) {
|
||||
bitmap = new std::bitset<N>;
|
||||
}
|
||||
~SyncholizedHeapBitset(void) {
|
||||
delete bitmap;
|
||||
}
|
||||
|
||||
bool test(size_t pos) {return bitmap->test(pos);}
|
||||
void set(size_t pos, bool val) {bitmap->set(pos, val);}
|
||||
size_t count(void) {return bitmap->count();}
|
||||
|
||||
private:
|
||||
std::bitset<N>* bitmap;
|
||||
};
|
||||
|
||||
void searchBlock(
|
||||
std::mutex& lock,
|
||||
std::vector<Recipe>& recipes,// readonly
|
||||
SyncholizedHeapBitset<(2<<24)>& colormap, // thread-safe
|
||||
std::vector<Color>& oldColors, // read-only
|
||||
std::vector<Color>& newColors, //write-only
|
||||
std::fstream& searched, // write-only.
|
||||
size_t start, size_t end
|
||||
) {
|
||||
for (size_t i = start; i < end; i++)
|
||||
{
|
||||
for(size_t j = 0; j < recipes.size(); j++) {
|
||||
Color crafted = recipes[j].getCraftedArmorColor(oldColors[i]);
|
||||
uint32_t encodedColor = crafted.encode();
|
||||
if (!colormap.test(encodedColor)) {
|
||||
lock.lock(); // for thread safety.
|
||||
if (!colormap.test(encodedColor)) { // double check after locked to avoid duplication.
|
||||
colormap.set(encodedColor, true);
|
||||
newColors.push_back(crafted);
|
||||
uint32_t encodedOldColor = oldColors[i].encode();
|
||||
searched.write((const char*) &encodedOldColor, sizeof(encodedOldColor));
|
||||
uint64_t encodedRecipe = recipes[j].encode();
|
||||
searched.write((const char*) &encodedRecipe, sizeof(encodedRecipe));
|
||||
searched.write((const char*) &encodedColor, sizeof(encodedColor));
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void searchAllColors(const std::vector<Color>& colorlist, int numThreads=8, int blockSize=32) {
|
||||
std::cout<<"preparing..."<<std::endl;
|
||||
// generate all possible recipes for a leather.
|
||||
std::vector<Recipe> recipes;
|
||||
std::vector<std::vector<int>> ans;
|
||||
std::vector<int> current;
|
||||
std::cout<<"Generating compose..."<<std::endl;
|
||||
generateCompose(24, 16, current, ans);
|
||||
size_t counts = 0;
|
||||
std::cout<<"Converting compose to recipe..."<<std::endl;
|
||||
for (size_t i = 0; i < ans.size(); i++)
|
||||
{
|
||||
Recipe recipe(composeToRecipe(ans[i], 25));
|
||||
if (recipe.encode() == 0) continue; // this recipe is empty.
|
||||
recipes.push_back(recipe);
|
||||
counts += 1;
|
||||
}
|
||||
std::cout<<"we have find "<<counts<<" possible recipes."<<std::endl;
|
||||
// pre-craft all possible recipes to save time.
|
||||
std::cout<<"Pre-crafting recipe..."<<std::endl;
|
||||
for (size_t i = 0; i < recipes.size(); i++)
|
||||
{
|
||||
recipes[i].preCraft(colorlist);
|
||||
}
|
||||
|
||||
// lets start to search.
|
||||
SyncholizedHeapBitset<(2<<24)> colormap;
|
||||
std::vector<Color> oldColors;
|
||||
// color #0x000000 do not exist in minecraft, so it is okay to be used as empty color.
|
||||
oldColors.push_back(Color(uint32_t(0)));
|
||||
std::mutex lock;
|
||||
|
||||
size_t layer = 1;
|
||||
while (oldColors.size() > 0) {
|
||||
std::cout<<"processing layer "<<layer<<std::endl;
|
||||
std::fstream output;
|
||||
std::string savepath = std::string("layer") + std::to_string(layer) + std::string(".bin");
|
||||
std::cout<<"writing generated recipe in this layer to file "<<savepath<<"..."<<std::endl;
|
||||
output.open(
|
||||
savepath,
|
||||
std::fstream::out|std::fstream::binary
|
||||
);
|
||||
std::vector<Color> newColors;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto last = start;
|
||||
for (size_t i = 0; i < oldColors.size(); i+=numThreads*blockSize)
|
||||
{
|
||||
// create threads and dispatch jobs.
|
||||
std::vector<std::thread*> threads;
|
||||
for(int j = 0; j < numThreads; j++) {
|
||||
int start = i + j*blockSize;
|
||||
int end = i + (j+1)*blockSize;
|
||||
if (end > (int) oldColors.size()) {
|
||||
end = (int) oldColors.size();
|
||||
}
|
||||
if (start > end) {start = end;}
|
||||
std::thread* t = new std::thread(
|
||||
searchBlock,
|
||||
std::ref(lock),
|
||||
std::ref(recipes),
|
||||
std::ref(colormap),
|
||||
std::ref(oldColors),
|
||||
std::ref(newColors),
|
||||
std::ref(output),
|
||||
start, end
|
||||
);
|
||||
threads.push_back(t);
|
||||
}
|
||||
// wait for all threads to finish.
|
||||
for(size_t j = 0; j < threads.size(); j++) {
|
||||
threads[j]->join();
|
||||
delete threads[j];
|
||||
threads[j] = nullptr;
|
||||
}
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> diff = now - start;
|
||||
std::chrono::duration<double> interval = now - last;
|
||||
if (interval.count() >= 5) {
|
||||
last = now;
|
||||
std::cout<<"progress: "<<double((i+1)*100)/oldColors.size()<<"%, ";
|
||||
std::cout<<colormap.count()<<" colors found.";
|
||||
double totalTime = diff.count()*oldColors.size()/(i+1);
|
||||
double remeaningTime = totalTime * (1 - (i+1)*1.0/oldColors.size());
|
||||
std::cout<<"estimating "<<totalTime<<" s in total and "<<remeaningTime<<" s left."<<std::endl;
|
||||
}
|
||||
}
|
||||
output.close();
|
||||
oldColors = newColors;
|
||||
std::cout<<"layer "<<layer<<" finished, found "<<oldColors.size()<<" new colors."<<std::endl;
|
||||
layer++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
std::vector<Color> colors = {
|
||||
Color(176, 46, 38),
|
||||
Color(94, 124, 22),
|
||||
Color(137, 50, 184),
|
||||
Color(22, 156, 156),
|
||||
Color(157, 157, 151),
|
||||
Color(71, 79, 82),
|
||||
Color(243, 139, 170),
|
||||
Color(128, 199, 31),
|
||||
Color(254, 216, 61),
|
||||
Color(58, 179, 218),
|
||||
Color(199, 78, 189),
|
||||
Color(249, 128, 29),
|
||||
Color(29, 29, 33),
|
||||
Color(131, 84, 50),
|
||||
Color(60, 68, 170),
|
||||
Color(249, 255, 254),
|
||||
};
|
||||
|
||||
// useless in this script, but useful for interpreting encoded recipe.
|
||||
std::vector<std::string> keys = {
|
||||
"red", "green", "purple", "cyan",
|
||||
"silver", "gray", "pink", "lime",
|
||||
"yellow", "lightBlue", "magenta", "orange",
|
||||
"black", "brown", "blue", "white"
|
||||
};
|
||||
|
||||
// parse numThreads.
|
||||
if (argc == 2) {
|
||||
int numThreads = std::stoi(std::string(argv[1]));
|
||||
searchAllColors(colors, numThreads);
|
||||
} else {
|
||||
std::cout<<"Usage: ./leatherColorCombinationsCalculator <numThreads>"<<std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
20
utils.py
Normal file
20
utils.py
Normal file
@ -0,0 +1,20 @@
|
||||
import json
|
||||
|
||||
def save_dict(path, json_dict, indent=4, mute=True):
|
||||
if not mute:
|
||||
print('dumping json string...')
|
||||
text = json.dumps(json_dict, indent=indent, ensure_ascii=False)
|
||||
if not mute:
|
||||
print('saving to {0}...'.format(path))
|
||||
with open(path, 'w+', encoding='utf-8') as f:
|
||||
f.write(text)
|
||||
|
||||
def load_dict(path, mute=True):
|
||||
if not mute:
|
||||
print('reading file {0}...'.format(path))
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
text = f.read()
|
||||
if not mute:
|
||||
print('parsing json...')
|
||||
json_dict = json.loads(text)
|
||||
return json_dict
|
||||
Loading…
Reference in New Issue
Block a user