Skip to content

Commit

Permalink
feat: talisman of insolence (#159)
Browse files Browse the repository at this point in the history
* feat: add format converter

* feat: picking out of many results

* feat: adjust enchant for v2

* test: add equal config tests for v2

* feat: migrate definitions to v2 config

* refactor: get rid of v1 config

* feat: add runs for talisman of insolence

* feat: add run for talisman of insolence
  • Loading branch information
ValeriiVasin committed Jun 18, 2022
1 parent 73836de commit e90b299
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 77 deletions.
7 changes: 2 additions & 5 deletions enchant/agathion.ts
Expand Up @@ -26,14 +26,11 @@ export const upToHighQualityAgathion: EnchantMap = new Map([
Item.Agathion_10,
{
item: Item.Agathion_10,
required: new Map([
cost: new Map([
[Item.Adena, 3_000_000],
[Item.AgathionSoulStone, 1],
]),
successRate: 100,
success: Item.HighQualityAgathion,
// not important
fail: Item.Nothing,
results: [{ chance: 100, item: Item.HighQualityAgathion }],
},
],
]);
Expand Down
14 changes: 4 additions & 10 deletions enchant/dragon-pendant.ts
Expand Up @@ -21,11 +21,8 @@ export const dragonPendantEnchantMap = new Map([
Item.DragonPendantOne_5,
{
item: Item.DragonPendantOne_5,
required: new Map([[Item.Adena, 12_000_000]]),
successRate: 100,
success: Item.DragonPendantTwo,
// not important
fail: Item.Nothing,
cost: new Map([[Item.Adena, 12_000_000]]),
results: [{ chance: 100, item: Item.DragonPendantTwo }],
},
],

Expand All @@ -47,14 +44,11 @@ export const dragonPendantEnchantMap = new Map([
Item.DragonPendantTwo_5,
{
item: Item.DragonPendantTwo_5,
required: new Map([
cost: new Map([
[Item.Adena, 16_000_000],
[Item.DragonVarnish, 3],
]),
successRate: 100,
success: Item.DragonPendantThree,
// not important
fail: Item.Nothing,
results: [{ chance: 100, item: Item.DragonPendantThree }],
},
],

Expand Down
32 changes: 17 additions & 15 deletions enchant/index.ts
Expand Up @@ -22,27 +22,29 @@ import { runeHardinEnchantMap } from './rune-hardin';
import { talismanOfAdenEnchantMap } from './talisman-of-aden';
import { talismanOfAuthorityEnchantMap } from './talisman-of-authority';
import { talismanOfEvaEnchantMap } from './talisman-of-eva';
import { talismanOfInsolenceEnchantMap } from './talisman-of-insolence';

export const enchants: EnchantMap = new Map([
...agathionEnchantMap,
...benirEnchantMap,
...bigRubyEnchantMap,
...bopEnchantMap,
...broochEnchantMap,
...chocolateEnchantMap,
...cloakEnchantMap,
...talismanOfAdenEnchantMap,
...talismanOfEvaEnchantMap,
...dragonBeltEnchantMap,
...dragonPendantEnchantMap,
...talismanOfAuthorityEnchantMap,
...runeAdenEnchantMap,
...agathionEnchantMap,
...upToHighQualityAgathion,
...highQualityAgathionEnchantMap,
...eventDragonSlayerEnchantMap,
...hatEnchantMap,
...heroCrownEnchantMap,
...dragonBeltEnchantMap,
...highQualityAgathionEnchantMap,
...ignisNecklaceEnchantMap,
...bopEnchantMap,
...broochEnchantMap,
...runeHardinEnchantMap,
...rubyEnchantMap,
...chocolateEnchantMap,
...benirEnchantMap,
...bigRubyEnchantMap,
...eventDragonSlayerEnchantMap,
...runeAdenEnchantMap,
...runeHardinEnchantMap,
...talismanOfAdenEnchantMap,
...talismanOfAuthorityEnchantMap,
...talismanOfEvaEnchantMap,
...talismanOfInsolenceEnchantMap,
...upToHighQualityAgathion,
]);
76 changes: 76 additions & 0 deletions enchant/talisman-of-insolence.ts
@@ -0,0 +1,76 @@
import { Item } from '../items';
import { EnchantMap } from '../types';

// https://forums.lineage2.com/topic/10049-tower-of-insolence/?do=findComment&comment=76516
export const talismanOfInsolenceEnchantMap: EnchantMap = new Map([
[
Item.TalismanOfInsolence_1,
{
item: Item.TalismanOfInsolence_1,
cost: new Map([
[Item.ElementalStone, 40],
[Item.EnergyOfInsolence, 2],
[Item.TalismanCrystal, 30],
]),
results: [
{ chance: 20, item: Item.TalismanOfInsolence_1 },
{ chance: 80, item: Item.TalismanOfInsolence_2 },
],
},
],
[
Item.TalismanOfInsolence_2,
{
item: Item.TalismanOfInsolence_2,
cost: new Map([
[Item.ElementalStone, 100],
[Item.EnergyOfInsolence, 3],
[Item.TalismanCrystal, 45],
]),
results: [
{ chance: 40, item: Item.TalismanOfInsolence_2 },
{ chance: 60, item: Item.TalismanOfInsolence_3 },
],
},
],
[
Item.TalismanOfInsolence_3,
{
item: Item.TalismanOfInsolence_3,
cost: new Map([
[Item.ElementalStone, 100],
[Item.EnergyOfInsolence, 4],
[Item.TalismanCrystal, 70],
]),
results: [
{ chance: 10, item: Item.TalismanOfInsolence_2 },
{ chance: 40, item: Item.TalismanOfInsolence_4 },
{ chance: 50, item: Item.TalismanOfInsolence_3 },
],
},
],
[
Item.TalismanOfInsolence_4,
{
item: Item.TalismanOfInsolence_4,
cost: new Map([
[Item.ElementalStone, 200],
[Item.EnergyOfInsolence, 5],
[Item.TalismanCrystal, 100],
]),
results: [
{ chance: 10, item: Item.TalismanOfInsolence_5 },
{ chance: 40, item: Item.TalismanOfInsolence_3 },
{ chance: 50, item: Item.TalismanOfInsolence_4 },
],
},
],
[
Item.TalismanOfInsolence_5,
{
item: Item.TalismanOfInsolence_5,
cost: new Map([[Item.Bx, 5_000]]),
results: [{ chance: 100, item: Item.TalismanOfInsolence_6 }],
},
],
]);
9 changes: 5 additions & 4 deletions helpers/create-enchant-map.ts
Expand Up @@ -30,10 +30,11 @@ export function createEnchantMap({

result.set(item, {
item,
required: requiredItem,
success: successItem,
fail: failItem,
successRate: chance,
cost: requiredItem,
results: [
{ chance, item: successItem },
{ chance: 100 - chance, item: failItem },
],
});
}

Expand Down
83 changes: 53 additions & 30 deletions helpers/enchant.spec.ts
@@ -1,27 +1,43 @@
import { Item } from '../items';
import { EnchantMap } from '../types';
import { enchant } from './enchant';
import { isEnchanted } from './is-enchanted';

jest.mock('./is-enchanted');
const mockedIsEnchanted = jest.mocked(isEnchanted);

// success and fails are based on the fact that
// success item is located first in convert-enchants.ts
describe('enchanting', () => {
let random: jest.SpyInstance;
let enchantSuccess: () => void;
let enchantFail: (chance: number) => void;

beforeEach(() => {
random = jest.spyOn(Math, 'random');

enchantSuccess = () => random.mockReturnValueOnce(0);
enchantFail = (chance: number) => {
random.mockReturnValueOnce(chance / 100);
};
});

afterEach(() => {
random.mockClear();
});

it('simple enchant', () => {
const enchants: EnchantMap = new Map([
[
Item.Agathion_1,
{
item: Item.Agathion_1,
successRate: 100,
required: Item.AgathionEnchantScroll,
success: Item.Agathion_2,
fail: Item.Agathion_1,
cost: Item.AgathionEnchantScroll,
results: [
{ chance: 50, item: Item.Agathion_2 },
{ chance: 50, item: Item.Agathion_1 },
],
},
],
]);

mockedIsEnchanted.mockReturnValue(true);
enchantSuccess();
expect(enchant(Item.Agathion_1, Item.Agathion_2, 1, enchants)).toEqual(
new Map([
[Item.Agathion_1, 1],
Expand All @@ -36,15 +52,16 @@ describe('enchanting', () => {
Item.Agathion_1,
{
item: Item.Agathion_1,
successRate: 100,
required: Item.Nothing,
success: Item.Agathion_2,
fail: Item.Agathion_1,
cost: Item.Nothing,
results: [
{ chance: 50, item: Item.Agathion_2 },
{ chance: 50, item: Item.Agathion_1 },
],
},
],
]);

mockedIsEnchanted.mockReturnValue(true);
enchantSuccess();
expect(enchant(Item.Agathion_1, Item.Agathion_2, 1, enchants)).toEqual(
new Map([[Item.Agathion_1, 1]]),
);
Expand All @@ -56,16 +73,18 @@ describe('enchanting', () => {
Item.Agathion_1,
{
item: Item.Agathion_1,
successRate: 100,
required: Item.AgathionEnchantScroll,
success: Item.Agathion_2,
fail: Item.Nothing,
cost: Item.AgathionEnchantScroll,
results: [
{ item: Item.Agathion_2, chance: 50 },
{ item: Item.Nothing, chance: 50 },
],
},
],
]);

// 1 failure, 1 success
mockedIsEnchanted.mockReturnValueOnce(false).mockReturnValueOnce(true);
enchantFail(50);
enchantSuccess();

expect(enchant(Item.Agathion_1, Item.Agathion_2, 1, enchants)).toEqual(
new Map([
Expand All @@ -81,15 +100,19 @@ describe('enchanting', () => {
Item.Agathion_1,
{
item: Item.Agathion_1,
successRate: 100,
required: new Map([[Item.AgathionEnchantScroll, 1]]),
success: Item.Agathion_2,
fail: Item.Agathion_1,
cost: new Map([[Item.AgathionEnchantScroll, 1]]),
results: [
{
item: Item.Agathion_2,
chance: 50,
},
{ item: Item.Agathion_1, chance: 50 },
],
},
],
]);

mockedIsEnchanted.mockReturnValue(true);
enchantSuccess();
expect(enchant(Item.Agathion_1, Item.Agathion_2, 1, enchants)).toEqual(
new Map([
[Item.Agathion_1, 1],
Expand All @@ -104,19 +127,19 @@ describe('enchanting', () => {
Item.TalismanOfAuthority_5,
{
item: Item.TalismanOfAuthority_5,
successRate: 100,
required: new Map([
cost: new Map([
[Item.Adena, 20_000_000],
[Item.TalismanOfAuthorityFragment, 3],
]),
success: Item.TalismanOfAuthority_6,
fail: Item.Nothing,
results: [
{ item: Item.TalismanOfAuthority_6, chance: 50 },
{ item: Item.Nothing, chance: 50 },
],
},
],
]);

mockedIsEnchanted.mockReturnValueOnce(true);

enchantSuccess();
expect(
enchant(
Item.TalismanOfAuthority_5,
Expand Down
15 changes: 6 additions & 9 deletions helpers/enchant.ts
@@ -1,30 +1,27 @@
import { enchants } from '../enchant';
import { Item } from '../items';
import { isEnchanted } from './is-enchanted';
import { mergeMaps } from './merge-maps';
import { pickEnchantResult } from './pick-enchant-result';

export const enchant = (
from: Item,
to: Item,
times = 1000,
enchantMap = enchants,
) => {
): Map<Item, number> => {
let used = new Map<Item, number>();

for (let i = 0; i < times; i++) {
let item = from;
used = inc(used, item);

while (item !== to) {
const currentEnchant = enchantMap.get(item)!;
used = inc(used, currentEnchant.required);
let currentEnchant = enchantMap.get(item)!;

if (isEnchanted(currentEnchant.successRate)) {
item = currentEnchant.success;
continue;
}
used = inc(used, currentEnchant.cost);

item = currentEnchant.fail;
const result = pickEnchantResult(currentEnchant.results);
item = result.item;

if (item !== Item.Nothing) {
continue;
Expand Down

0 comments on commit e90b299

Please sign in to comment.