Skip to content

Commit 4ca57cc

Browse files
committed
Fix CivilisationEncoder for big numbers
Apparently, 54 civs are one too many.
1 parent c19e40b commit 4ca57cc

File tree

3 files changed

+121
-15
lines changed

3 files changed

+121
-15
lines changed

src/__tests__/models/CivilisationEncoder.test.tsx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@ it('vikings only yields 2^30', () => {
1919
expect(encoded).toEqual('40000000');
2020
});
2121

22-
it('all 45+1+3 civs yield 2^49-1', () => {
22+
it('all 45+1+3+5 civs yield 2^54-1', () => {
2323
const encoded = CivilisationEncoder.encodeCivilisationArray(Civilisation.ALL);
24-
expect(Civilisation.ALL.length).toEqual(45+1+3);
24+
expect(Civilisation.ALL.length).toEqual(45+1+3+5);
2525
expect(Civilisation.ALL_ACTIVE.length).toEqual(45);
26-
expect(encoded).toEqual(CivilisationEncoder.toHexString(Math.pow(2, 49) - 1));
27-
expect(encoded).toEqual('1ffffffffffff');
26+
expect(encoded).toEqual('3fffffffffffff');
2827
});
2928

3029
it('decode 0 yields empty array', () => {
@@ -44,8 +43,8 @@ it('decode 2^30 yields vikings', () => {
4443

4544
});
4645

47-
it('decode 2^49-1 yields all civs', () => {
48-
const decoded = CivilisationEncoder.decodeCivilisationArray('1ffffffffffff');
46+
it('decode 2^54-1 yields all civs', () => {
47+
const decoded = CivilisationEncoder.decodeCivilisationArray('3fffffffffffff');
4948
expect(decoded).toEqual([...Civilisation.ALL].sort((a, b) => a.name.localeCompare(b.name)));
5049
});
5150

@@ -58,6 +57,7 @@ it('assert order of civilisations has not changed', () => {
5857
expect(Civilisation.ALL).toMatchSnapshot();
5958
});
6059

60+
6161
describe('The decoding shortcut does not lie', () => {
6262
it.each`
6363
civilisationArray | expectedLength
@@ -74,3 +74,21 @@ describe('The decoding shortcut does not lie', () => {
7474
expect(decoded.length).toEqual(expectedLength);
7575
});
7676
});
77+
78+
describe('toBits works well', () => {
79+
it.each`
80+
encoded | result
81+
${''} | ${[]}
82+
${'1'} | ${[true]}
83+
${'2'} | ${[true, false]}
84+
${'3'} | ${[true, true]}
85+
${'4'} | ${[true, false, false]}
86+
${'8'} | ${[true, false, false, false]}
87+
${'f'} | ${[true, true, true, true]}
88+
${'3f'} | ${[true, true, true, true, true, true]}
89+
${'ff'} | ${[true, true, true, true, true, true, true, true]}
90+
`('$civilisationArray', ({encoded, result}) => {
91+
const bits = CivilisationEncoder.toBits(encoded);
92+
expect(bits).toEqual(result);
93+
});
94+
});

src/__tests__/models/__snapshots__/CivilisationEncoder.test.tsx.snap

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,5 +639,70 @@ Array [
639639
},
640640
"name": "Spartans",
641641
},
642+
Civilisation {
643+
"category": "default",
644+
"gameVersion": 13,
645+
"i18nPrefix": "civs.",
646+
"id": "Shu",
647+
"imageUrls": Object {
648+
"animated_left": "/images/units-animated/shu-left.apng",
649+
"animated_right": "/images/units-animated/shu-right.apng",
650+
"emblem": "/images/civemblems/shu.png",
651+
"unit": "/images/civs/shu.png",
652+
},
653+
"name": "Shu",
654+
},
655+
Civilisation {
656+
"category": "default",
657+
"gameVersion": 13,
658+
"i18nPrefix": "civs.",
659+
"id": "Wu",
660+
"imageUrls": Object {
661+
"animated_left": "/images/units-animated/wu-left.apng",
662+
"animated_right": "/images/units-animated/wu-right.apng",
663+
"emblem": "/images/civemblems/wu.png",
664+
"unit": "/images/civs/wu.png",
665+
},
666+
"name": "Wu",
667+
},
668+
Civilisation {
669+
"category": "default",
670+
"gameVersion": 13,
671+
"i18nPrefix": "civs.",
672+
"id": "Wei",
673+
"imageUrls": Object {
674+
"animated_left": "/images/units-animated/wei-left.apng",
675+
"animated_right": "/images/units-animated/wei-right.apng",
676+
"emblem": "/images/civemblems/wei.png",
677+
"unit": "/images/civs/wei.png",
678+
},
679+
"name": "Wei",
680+
},
681+
Civilisation {
682+
"category": "default",
683+
"gameVersion": 13,
684+
"i18nPrefix": "civs.",
685+
"id": "Jurchens",
686+
"imageUrls": Object {
687+
"animated_left": "/images/units-animated/jurchens-left.apng",
688+
"animated_right": "/images/units-animated/jurchens-right.apng",
689+
"emblem": "/images/civemblems/jurchens.png",
690+
"unit": "/images/civs/jurchens.png",
691+
},
692+
"name": "Jurchens",
693+
},
694+
Civilisation {
695+
"category": "default",
696+
"gameVersion": 13,
697+
"i18nPrefix": "civs.",
698+
"id": "Khitans",
699+
"imageUrls": Object {
700+
"animated_left": "/images/units-animated/khitans-left.apng",
701+
"animated_right": "/images/units-animated/khitans-right.apng",
702+
"emblem": "/images/civemblems/khitans.png",
703+
"unit": "/images/civs/khitans.png",
704+
},
705+
"name": "Khitans",
706+
},
642707
]
643708
`;

src/util/CivilisationEncoder.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@ import Civilisation from "../models/Civilisation";
22

33
export const CivilisationEncoder = {
44
encodeCivilisationArray(civilisations: Civilisation[]): string {
5-
let encoded: number = 0;
5+
let value = '';
66
const civilisationIds = civilisations.map(value => value.id);
7-
for (let i = 0; i < Civilisation.ALL.length; i++) {
8-
if (civilisationIds.includes(Civilisation.ALL[i].id)) {
9-
encoded += Math.pow(2, i);
7+
for (let i = 0; i < Civilisation.ALL.length; i += 4) {
8+
let encoded: number = 0;
9+
for (let j = 0; j < 4; j++) {
10+
const index = i + j;
11+
if (index < Civilisation.ALL.length && civilisationIds.includes(Civilisation.ALL[index].id)) {
12+
encoded += Math.pow(2, j);
13+
}
1014
}
15+
value = CivilisationEncoder.toHexString(encoded) + value;
1116
}
12-
return CivilisationEncoder.toHexString(encoded);
17+
value = value.replace(/^0+/, '') || '0'
18+
return value;
1319
},
1420

1521
decodeCivilisationArray(encoded: string): Civilisation[] {
@@ -19,12 +25,10 @@ export const CivilisationEncoder = {
1925
return civilisations;
2026
}
2127
try {
22-
const encodedNumber = parseInt(encoded, 16);
23-
const binaryString: string = encodedNumber.toString(2);
24-
const bits: string[] = binaryString.split('');
28+
const bits = CivilisationEncoder.toBits(encoded);
2529
const civilisations = [];
2630
for (let i = 0; i < bits.length; i++) {
27-
if (bits[i] === '1') {
31+
if (bits[i]) {
2832
civilisations.push(Civilisation.ALL[bits.length - 1 - i]);
2933
}
3034
}
@@ -38,5 +42,24 @@ export const CivilisationEncoder = {
3842

3943
toHexString(input: number): string {
4044
return input.toString(16);
45+
},
46+
47+
toBits(encoded: string): boolean[] {
48+
const bits: boolean[] = []
49+
const items = encoded.split('');
50+
for (const item of items) {
51+
const number = parseInt(item, 16);
52+
if(isNaN(number)){
53+
return []
54+
}
55+
let binaryString = number.toString(2);
56+
binaryString = binaryString.padStart(4, '0');
57+
const stringBits = binaryString.split('');
58+
for (let i = 0; i < stringBits.length; i++) {
59+
bits.push(stringBits[i] === '1');
60+
}
61+
}
62+
const first = bits.indexOf(true);
63+
return bits.slice(first);
4164
}
4265
};

0 commit comments

Comments
 (0)