Skip to content

Commit 89f5eab

Browse files
authored
Merge pull request #435 from akashic-games/update-xorshift
feat: xorshift のリファレンス実装を更新
2 parents ec66c8a + b98f32b commit 89f5eab

File tree

8 files changed

+68
-55
lines changed

8 files changed

+68
-55
lines changed

.github/workflows/reftest.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ jobs:
99
strategy:
1010
matrix:
1111
os: [ubuntu-latest]
12-
node: [14.x, 16.x]
12+
node: [16.x, 18.x]
1313
steps:
1414
- uses: actions/setup-node@v3
1515
with:
1616
node-version: ${{ matrix.node }}
17-
- name: Use npm@7
18-
run: npm i -g npm@7 --registry=https://registry.npmjs.org
1917
- name: Checkout akashic-engine repository
2018
uses: actions/checkout@v3
2119
with:

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
- main
77

88
env:
9-
NODE_VERSION: 16
9+
NODE_VERSION: 18
1010

1111
jobs:
1212
release:

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
strategy:
1010
matrix:
1111
os: [ubuntu-latest, windows-latest]
12-
node: [14.x, 16.x]
12+
node: [16.x, 18.x]
1313
steps:
1414
- name: Checkout code
1515
uses: actions/checkout@v3

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
# ChangeLog
22

3-
## unreleased changes
3+
## 3.10.0
44
* @akashic/game-configuration@1.9.0 に追従
5+
* xorshift のリファレンス実装を更新
6+
7+
### ゲーム開発者への影響
8+
* xorshift のリファレンス実装の更新により、既存の playlog の再現性が失われる可能性があります。
59

610
## 3.9.3
711
機能追加

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@akashic/akashic-engine",
3-
"version": "3.9.3",
3+
"version": "3.10.0",
44
"description": "The core library of Akashic Engine",
55
"main": "index.js",
66
"dependencies": {
@@ -33,7 +33,7 @@
3333
"ts-jest": "^29.0.0",
3434
"typedoc": "^0.23.0",
3535
"typescript": "^4.7.4",
36-
"xorshift": "0.2.0"
36+
"xorshift": "1.2.0"
3737
},
3838
"scripts": {
3939
"prepare": "npm run build && npm run doc",

src/Xorshift.ts

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,48 +4,43 @@
44
// Arranged by DWANGO Co., Ltd.
55

66
export class Xorshift {
7-
private _state0U!: number;
8-
private _state0L!: number;
9-
private _state1U!: number;
10-
private _state1L!: number;
7+
private _state0U: number;
8+
private _state0L: number;
9+
private _state1U: number;
10+
private _state1L: number;
1111

1212
static deserialize(ser: XorshiftSerialization): Xorshift {
13-
const ret = new Xorshift(0);
14-
ret._state0U = ser._state0U;
15-
ret._state0L = ser._state0L;
16-
ret._state1U = ser._state1U;
17-
ret._state1L = ser._state1L;
13+
const ret = new Xorshift([ser._state0U, ser._state0L, ser._state1U, ser._state1L]);
1814
return ret;
1915
}
2016

21-
constructor(seed: number) {
22-
this.initState(seed);
17+
constructor(seed: number | [number, number, number, number]) {
18+
const seeds = Array.isArray(seed) ? seed : this.generateSeeds(seed);
19+
20+
this._state0U = seeds[0] | 0;
21+
this._state0L = seeds[1] | 0;
22+
this._state1U = seeds[2] | 0;
23+
this._state1L = seeds[3] | 0;
2324
}
2425

25-
// シード値が1つの場合にどのようにして初期状態を定義するかは特に定まっていない
26-
// このコードはロジック的な裏付けは無いが採用例が多いために採用した
27-
// 以下採用例
28-
// http://meme.biology.tohoku.ac.jp/klabo-wiki/index.php?cmd=read&page=%B7%D7%BB%BB%B5%A1%2FC%2B%2B#y919a7e1
29-
// http://hexadrive.sblo.jp/article/63660775.html
30-
// http://meme.biology.tohoku.ac.jp/students/iwasaki/cxx/random.html#xorshift
3126
initState(seed: number): void {
32-
const factor = 1812433253;
33-
seed = factor * (seed ^ (seed >> 30)) + 1;
34-
this._state0U = seed;
35-
seed = factor * (seed ^ (seed >> 30)) + 2;
36-
this._state0L = seed;
37-
seed = factor * (seed ^ (seed >> 30)) + 3;
38-
this._state1U = seed;
39-
seed = factor * (seed ^ (seed >> 30)) + 4;
40-
this._state1L = seed;
27+
const seeds = this.generateSeeds(seed);
28+
this._state0L = seeds[0] | 0;
29+
this._state0U = seeds[1] | 0;
30+
this._state1L = seeds[2] | 0;
31+
this._state1U = seeds[3] | 0;
4132
}
4233

43-
randomInt(): number[] {
34+
randomInt(): [number, number] {
4435
let s1U = this._state0U;
4536
let s1L = this._state0L;
4637
const s0U = this._state1U;
4738
const s0L = this._state1L;
4839

40+
const sumL = (s0L >>> 0) + (s1L >>> 0);
41+
const resU = (s0U + s1U + ((sumL / 2) >>> 31)) >>> 0;
42+
const resL = sumL >>> 0;
43+
4944
this._state0U = s0U;
5045
this._state0L = s0L;
5146

@@ -64,14 +59,14 @@ export class Xorshift {
6459
t1U = s1U ^ s0U;
6560
t1L = s1L ^ s0L;
6661

67-
const a2 = 17;
62+
const a2 = 18;
6863
const m2 = 0xffffffff >>> (32 - a2);
6964
t2U = s1U >>> a2;
7065
t2L = (s1L >>> a2) | ((s1U & m2) << (32 - a2));
7166
t1U = t1U ^ t2U;
7267
t1L = t1L ^ t2L;
7368

74-
const a3 = 26;
69+
const a3 = 5;
7570
const m3 = 0xffffffff >>> (32 - a3);
7671
t2U = s0U >>> a3;
7772
t2L = (s0L >>> a3) | ((s0U & m3) << (32 - a3));
@@ -81,16 +76,14 @@ export class Xorshift {
8176
this._state1U = t1U;
8277
this._state1L = t1L;
8378

84-
const sumL = (t1L >>> 0) + (s0L >>> 0);
85-
t2U = (t1U + s0U + ((sumL / 2) >>> 31)) >>> 0;
86-
t2L = sumL >>> 0;
87-
88-
return [t2U, t2L];
79+
return [resU, resL];
8980
}
9081

9182
random(): number {
9283
const t2 = this.randomInt();
93-
return (t2[0] * 4294967296 + t2[1]) / 18446744073709551616;
84+
// Math.pow(2, -32) = 2.3283064365386963e-10
85+
// Math.pow(2, -52) = 2.220446049250313e-16
86+
return t2[0] * 2.3283064365386963e-10 + (t2[1] >>> 12) * 2.220446049250313e-16;
9487
}
9588

9689
nextInt(min: number, sup: number): number {
@@ -105,12 +98,30 @@ export class Xorshift {
10598
_state1L: this._state1L
10699
};
107100
}
101+
102+
// シード値が1つの場合にどのようにして初期状態を定義するかは特に定まっていない
103+
// このコードはロジック的な裏付けは無いが採用例が多いために採用した
104+
// 以下採用例
105+
// http://meme.biology.tohoku.ac.jp/klabo-wiki/index.php?cmd=read&page=%B7%D7%BB%BB%B5%A1%2FC%2B%2B#y919a7e1
106+
// http://hexadrive.sblo.jp/article/63660775.html
107+
// http://meme.biology.tohoku.ac.jp/students/iwasaki/cxx/random.html#xorshift
108+
private generateSeeds(seed: number): [number, number, number, number] {
109+
const factor = 1812433253;
110+
seed = factor * (seed ^ (seed >> 30)) + 1;
111+
const seed1 = seed;
112+
seed = factor * (seed ^ (seed >> 30)) + 2;
113+
const seed2 = seed;
114+
seed = factor * (seed ^ (seed >> 30)) + 3;
115+
const seed3 = seed;
116+
seed = factor * (seed ^ (seed >> 30)) + 4;
117+
const seed4 = seed;
118+
return [seed1, seed2, seed3, seed4];
119+
}
108120
}
109121

110122
/**
111123
* serialize/deserialize用のインターフェース
112124
*/
113-
114125
export interface XorshiftSerialization {
115126
/**
116127
* @ignore

src/__tests__/XorshiftSpec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Xorshift } from "..";
22
import { customMatchers } from "./helpers";
3-
/* eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-var-requires */
4-
const RefXorshift = require("xorshift").constructor; // require と変数名でエラーとなるが、型定義がないのでコメントで無効とする。
3+
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-var-requires
4+
const RefXorshift = require("xorshift").constructor;
55

66
expect.extend(customMatchers);
77

0 commit comments

Comments
 (0)