init
This commit is contained in:
commit
458f217c30
8 changed files with 213 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/bin
|
||||||
|
/deno.lock
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"deno.enable": true
|
||||||
|
}
|
43
README.md
Normal file
43
README.md
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# reciper
|
||||||
|
|
||||||
|
A script to calculate recipe costs from concise recipe definition file.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
Usage:
|
||||||
|
reciper <recipe_file>
|
||||||
|
```
|
||||||
|
|
||||||
|
With the file `example/demo` as input :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
|
||||||
|
# main targets
|
||||||
|
3 a
|
||||||
|
2 b
|
||||||
|
|
||||||
|
# definition for a
|
||||||
|
a
|
||||||
|
2 c
|
||||||
|
3 b
|
||||||
|
|
||||||
|
# definition for b
|
||||||
|
b
|
||||||
|
1 c
|
||||||
|
|
||||||
|
# no definition for c
|
||||||
|
```
|
||||||
|
|
||||||
|
The output is :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ ./bin/reciper example/demo
|
||||||
|
# To produce :
|
||||||
|
# - 3 a
|
||||||
|
# - 2 b
|
||||||
|
#
|
||||||
|
# Get :
|
||||||
|
# - 17 c
|
||||||
|
#
|
||||||
|
```
|
6
build.sh
Executable file
6
build.sh
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$(realpath "$0")")"
|
||||||
|
|
||||||
|
mkdir -p bin
|
||||||
|
deno compile --output=bin/reciper --allow-read src/main.ts
|
6
deno.json
Normal file
6
deno.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"fmt": {
|
||||||
|
"useTabs": true,
|
||||||
|
"lineWidth": 120
|
||||||
|
}
|
||||||
|
}
|
17
example/demo
Normal file
17
example/demo
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# main targets
|
||||||
|
3 a
|
||||||
|
2 b
|
||||||
|
|
||||||
|
# definition for a
|
||||||
|
a
|
||||||
|
2 c
|
||||||
|
3 b
|
||||||
|
|
||||||
|
# definition for b
|
||||||
|
b
|
||||||
|
1 c
|
||||||
|
|
||||||
|
# no definition for c
|
||||||
|
|
||||||
|
# gives :
|
||||||
|
# to produce [ { item: "a", count: 3 }, { item: "b", count: 2 } ] get [ { item: "c", count: 17 } ]
|
57
example/minecraft
Normal file
57
example/minecraft
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# target declaration
|
||||||
|
1 digital miner
|
||||||
|
|
||||||
|
# all applicable recipes
|
||||||
|
digital miner
|
||||||
|
2 atomic alloy
|
||||||
|
1 basic control circuit
|
||||||
|
2 logistical sorter
|
||||||
|
1 robit
|
||||||
|
2 teleportation core
|
||||||
|
1 steel casing
|
||||||
|
|
||||||
|
atomic alloy
|
||||||
|
1 reinforced alloy
|
||||||
|
1 enriched obsidian
|
||||||
|
|
||||||
|
reinforced alloy
|
||||||
|
1 infused alloy
|
||||||
|
1 enriched diamond
|
||||||
|
|
||||||
|
infused alloy
|
||||||
|
1 iron ingot
|
||||||
|
1 enriched redstrone
|
||||||
|
|
||||||
|
logistical sorter
|
||||||
|
7 iron ingot
|
||||||
|
1 piston
|
||||||
|
1 basic control circuit
|
||||||
|
|
||||||
|
robit
|
||||||
|
1 steel ingot
|
||||||
|
1 atomic alloy
|
||||||
|
2 energy tablet
|
||||||
|
1 refined obsidian ingot
|
||||||
|
1 personal chest
|
||||||
|
|
||||||
|
energy tablet
|
||||||
|
4 redstrone
|
||||||
|
3 gold ingot
|
||||||
|
2 infused alloy
|
||||||
|
|
||||||
|
personal chest
|
||||||
|
5 steel ingot
|
||||||
|
2 chest
|
||||||
|
1 glass
|
||||||
|
1 basic control circuit
|
||||||
|
|
||||||
|
teleportation core
|
||||||
|
2 atomic alloy
|
||||||
|
4 lapis
|
||||||
|
2 gold
|
||||||
|
1 diamond
|
||||||
|
|
||||||
|
basic control circuit
|
||||||
|
1 osmium ingot
|
||||||
|
1 enriched redstrone
|
||||||
|
|
79
src/main.ts
Executable file
79
src/main.ts
Executable file
|
@ -0,0 +1,79 @@
|
||||||
|
#!/bin/env -S deno run --allow-read
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const { path } = parse_args(Deno.args);
|
||||||
|
const content = await Deno.readTextFile(path);
|
||||||
|
const { targets, definitions } = parse_file(content);
|
||||||
|
const ingredients = calculate_ingredients(targets, definitions);
|
||||||
|
const merged = merge_ingredients(ingredients);
|
||||||
|
console.log("To produce :\n", ...format_ingredients(targets), "\nGet :\n", ...format_ingredients(merged));
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse_args(args: string[]) {
|
||||||
|
const invalid_usage = () => Deno.exit(console.log("Usage:\nreciper <recipe_file>") as undefined);
|
||||||
|
if (args.length !== 1) invalid_usage();
|
||||||
|
const [path] = args;
|
||||||
|
if (["-h", "--help"].includes(path)) invalid_usage();
|
||||||
|
return { path };
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse_file(content: string) {
|
||||||
|
const lines = content.trim().split("\n");
|
||||||
|
const definitions = [] as Definition[];
|
||||||
|
const main = { target: "main", ingredients: [] } as Definition;
|
||||||
|
let current_block = null as Definition | null;
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.trim().startsWith("#")) continue;
|
||||||
|
if (line.trim() === "") {
|
||||||
|
if (current_block !== null) definitions.push(current_block);
|
||||||
|
current_block = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const [count_str, ...words] = line.split(" ");
|
||||||
|
const count = parseFloat(count_str);
|
||||||
|
if (isNaN(count)) {
|
||||||
|
if (current_block !== null) definitions.push(current_block);
|
||||||
|
current_block = null;
|
||||||
|
current_block = { target: line, ingredients: [] };
|
||||||
|
} else {
|
||||||
|
const item = words.join(" ");
|
||||||
|
if (current_block === null) main.ingredients.push({ item, count });
|
||||||
|
else current_block.ingredients.push({ item, count });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current_block !== null) definitions.push(current_block);
|
||||||
|
const targets = main.ingredients;
|
||||||
|
return { targets, definitions };
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ingredient = { item: string; count: number };
|
||||||
|
type Definition = { target: string; ingredients: Ingredient[] };
|
||||||
|
|
||||||
|
function calculate_ingredients(targets: Ingredient[], definitions: Definition[]) {
|
||||||
|
const result = [] as Ingredient[];
|
||||||
|
for (const target of targets) {
|
||||||
|
const recipe = definitions.find((recipe) => recipe.target === target.item);
|
||||||
|
if (recipe === undefined) {
|
||||||
|
result.push(target);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const defs = definitions.filter((def) => def.target !== target.item);
|
||||||
|
for (const ingredient of calculate_ingredients(recipe.ingredients, defs)) {
|
||||||
|
const count = ingredient.count * target.count;
|
||||||
|
result.push({ item: ingredient.item, count });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function merge_ingredients(ingredients: Ingredient[]): Ingredient[] {
|
||||||
|
const set = new Map<string, number>();
|
||||||
|
for (const { count, item } of ingredients) set.set(item, count + (set.get(item) ?? 0));
|
||||||
|
return Array.from(set.entries()).map(([item, count]) => ({ item, count }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function* format_ingredients(ingredients: Ingredient[]) {
|
||||||
|
for (const { count, item } of ingredients) yield* ["-", count, item, "\n"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.meta.main) await main();
|
Loading…
Add table
Add a link
Reference in a new issue