Skip to content

Commit

Permalink
fix: 修复大数字骰面概率偏移问题;
Browse files Browse the repository at this point in the history
imp: 计算类型现在能获取到详细的中间过程
  • Loading branch information
fy0 committed Sep 7, 2024
1 parent 28fab8c commit e4d40be
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 29 deletions.
39 changes: 28 additions & 11 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ type ParserData struct {
}
loopLayer int // 当前loop层数
codeStack []struct {
code []ByteCode
index int
code []ByteCode
index int
textPos int
}
}

Expand Down Expand Up @@ -286,8 +287,21 @@ func (e *ParserData) AddInvoke(paramsNum IntType) {
e.WriteCode(typeInvoke, paramsNum)
}

func fixCodeByOffset(code []ByteCode, offset int) {
for index, i := range code {
switch i.T {
case typeDetailMark:
v := i.Value.(BufferSpan)
v.Begin -= IntType(offset)
v.End -= IntType(offset)
code[index].Value = v
}
}
}

func (p *ParserData) AddStoreComputed(name string, text string) {
code, length := p.CodePop()
code, length, offset := p.CodePop()
fixCodeByOffset(code, offset)
val := NewComputedValRaw(&ComputedData{
Expr: text,
code: code,
Expand All @@ -299,7 +313,8 @@ func (p *ParserData) AddStoreComputed(name string, text string) {
}

func (p *ParserData) AddStoreComputedOnStack(text string) {
code, length := p.CodePop()
code, length, offset := p.CodePop()
fixCodeByOffset(code, offset)
val := NewComputedValRaw(&ComputedData{
Expr: text,
code: code,
Expand All @@ -310,7 +325,8 @@ func (p *ParserData) AddStoreComputedOnStack(text string) {
}

func (p *ParserData) AddStoreFunction(name string, paramsReversed []string, text string) {
code, length := p.CodePop()
code, length, offset := p.CodePop()
fixCodeByOffset(code, offset)

// 翻转一次
for i, j := 0, len(paramsReversed)-1; i < j; i, j = i+1, j-1 {
Expand Down Expand Up @@ -340,22 +356,23 @@ func (p *ParserData) AddAttrSet(objName string, attr string, isRaw bool) {
p.WriteCode(typeAttrSet, attr)
}

func (p *ParserData) CodePush() {
func (p *ParserData) CodePush(textPos int) {
p.codeStack = append(p.codeStack, struct {
code []ByteCode
index int
}{code: p.code, index: p.codeIndex})
code []ByteCode
index int
textPos int
}{code: p.code, index: p.codeIndex, textPos: textPos})
p.code = make([]ByteCode, 256)
p.codeIndex = 0
}

func (p *ParserData) CodePop() ([]ByteCode, int) {
func (p *ParserData) CodePop() ([]ByteCode, int, int) {
lastCode, lastIndex := p.code, p.codeIndex

last := len(p.codeStack) - 1
info := p.codeStack[last]
p.codeStack = p.codeStack[:last]
p.code = info.code
p.codeIndex = info.index
return lastCode, lastIndex
return lastCode, lastIndex, info.textPos
}
8 changes: 4 additions & 4 deletions roll.peg
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ stmtIf <- "if" sp1x (exprRoot sp { c.data.AddOp(typeBlockPush); c.data.AddOp(typ
func_def_params <- '(' sp ')' sp { c.data.CounterPush() }
/ '(' sp { c.data.CounterPush(); c.data.CounterAdd(1) } id:identifier sp { c.data.NamePush(id.(string)) } (',' sp id2:identifier sp { c.data.NamePush(id2.(string)) } {c.data.CounterAdd(1)} )* ')' sp

stmtFunc <- "func" sp1x id:identifier sp { c.data.NamePush(id.(string)) } func_def_params '{' sp { c.data.CodePush() } exprText:< stmtRoot? > '}' sp
stmtFunc <- "func" sp1x id:identifier sp { c.data.NamePush(id.(string)) } func_def_params '{' sp { c.data.CodePush(p.pt.offset) } exprText:< stmtRoot? > '}' sp
{ num := c.data.CounterPop(); arr := []string{}; for i:=IntType(0); i<num; i++ { arr = append(arr, c.data.NamePop()) }; c.data.AddStoreFunction(c.data.NamePop(), arr, exprText.(string)) }

// 赋值
stmtAssignType1 <- id:identifier sp { c.data.NamePush(id.(string)) } '=' sp exprRoot { c.data.AddStore(c.data.NamePop()) }
stmtAssignType2 <- '&' id:identifier sp { c.data.NamePush(id.(string)) } '=' sp { c.data.CodePush() } expr:<exprRoot> { c.data.AddStoreComputed(c.data.NamePop(), expr.(string)) }
stmtAssignType2 <- '&' id:identifier sp { c.data.NamePush(id.(string)) } '=' sp { c.data.CodePush(p.pt.offset) } expr:<exprRoot> { c.data.AddStoreComputed(c.data.NamePop(), expr.(string)) }
stmtAssignType3 <- '&' id:identifier sp { c.data.NamePush(id.(string)) } '.' id2:identifier sp { c.data.NamePush(id2.(string)) } sp '=' sp exprRoot { attr, objName := c.data.NamePop(), c.data.NamePop(); c.data.AddAttrSet(objName, attr, true) }
stmtAssignType4 <- "this" sp '.' sp id:identifier sp { c.data.NamePush(id.(string)) } '=' sp exprRoot { c.data.AddStoreLocal(c.data.NamePop()) }
stmtAssignType5 <- id:identifier sp { c.data.NamePush(id.(string)) } '.' sp id2:identifier sp { c.data.NamePush(id2.(string)) } '=' sp exprRoot { attr, objName := c.data.NamePop(), c.data.NamePop(); c.data.AddAttrSet(objName, attr, false) }
Expand Down Expand Up @@ -475,8 +475,8 @@ st_assign <- &(st_name2 sp (':' / '=') sp est) st_name2 sp (':' / '=') sp est {
/ &(st_name2r sp '*' sp (':' / '=') sp est) st_name2r sp '*' sp (':' / '=') sp est { c.data.AddOp(typeStX0) } // 属性*: 4
/ &(st_name2r sp (':' / '=') sp est) st_name2r sp (':' / '=') sp est { c.data.AddStName() } // 属性2: 70
/ &(st_name1r est) st_name1r est { c.data.AddStName() } // 力量60
/ &('&' st_name2 sp (':' / '=') est) '&' st_name2 sp (':' / '=') sp { c.data.CodePush(); } text:< est > { c.data.AddStoreComputedOnStack(text.(string)); c.data.AddStName() } // &手枪 = 1d4
/ &('&' st_name2r sp (':' / '=') est) '&' st_name2r sp (':' / '=') sp {c.data.CodePush(); } text:< est > { c.data.AddStoreComputedOnStack(text.(string)); c.data.AddStName() } // &手枪 = 1d4
/ &('&' st_name2 sp (':' / '=') est) '&' st_name2 sp (':' / '=') sp { c.data.CodePush(p.pt.offset); } text:< est > { c.data.AddStoreComputedOnStack(text.(string)); c.data.AddStName() } // &手枪 = 1d4
/ &('&' st_name2r sp (':' / '=') est) '&' st_name2r sp (':' / '=') sp { c.data.CodePush(p.pt.offset); } text:< est > { c.data.AddStoreComputedOnStack(text.(string)); c.data.AddStName() } // &手枪 = 1d4

// 第一类:
// 力量+1d3
Expand Down
8 changes: 4 additions & 4 deletions roll.peg.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 22 additions & 5 deletions roll_func.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ func Roll(src *rand.PCGSource, dicePoints IntType, mod int) IntType {
if dicePoints == 0 {
return 0
}
// 这里判断不了IntType的长度,但编译器会自动优化掉没用的分支
if IntTypeSize == 8 && dicePoints > math.MaxInt64-1 {

Check failure on line 28 in roll_func.go

View workflow job for this annotation

GitHub Actions / build

math.MaxInt64 - 1 (untyped int constant 9223372036854775806) overflows int
return 0
}
if IntTypeSize == 4 && dicePoints > math.MaxInt32-1 {
return 0
}

if mod == -1 {
return 1
}
Expand All @@ -33,11 +41,20 @@ func Roll(src *rand.PCGSource, dicePoints IntType, mod int) IntType {
if src == nil {
src = randSource
}
// js端有bug,如果当前IntType为32位,Int63会得到负数
// val := IntType(rand.Int31())%dicePoints + 1
// 同样问题也存在于这里,所以需要 &math.MaxInt32
val := IntType(src.Uint64()&math.MaxInt32)%dicePoints + 1
return val

v := src.Uint64() // 如果弄32位版本,可以写成 uint32(src.Uint64() >> 32)
n := uint64(dicePoints)
// 下面这段取整代码来自 golang 的 exp/rand
if n&(n-1) == 0 { // n is power of two, can mask
return IntType(v&(n-1) + 1)
}
if v > math.MaxUint64-n { // Fast check.
ceiling := math.MaxUint64 - math.MaxUint64%n
for v >= ceiling {
v = src.Uint64()
}
}
return IntType(v%n + 1)
}

func wodCheck(e *Context, addLine IntType, pool IntType, points IntType, threshold IntType) bool {
Expand Down
4 changes: 3 additions & 1 deletion rollvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ func (ctx *Context) evaluate() {
}

solveDetail := func() {
if ctx.subThreadDepth != 0 {
if !ctx.forceSolveDetail && ctx.subThreadDepth != 0 {
return
}
sort.Sort(spanByBegin(details))
Expand Down Expand Up @@ -1059,6 +1059,8 @@ func (ctx *Context) evaluate() {
}
}
}

solveDetail()
}

func (ctx *Context) GetAsmText() string {
Expand Down
6 changes: 4 additions & 2 deletions rollvm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1764,7 +1764,8 @@ func TestDetailTextComputed(t *testing.T) {
vm.Attrs.Store("a", NewComputedVal("4d1"))
err := vm.Run("a")
if assert.NoError(t, err) {
assert.Equal(t, "4[a=4d1=4]", vm.GetDetailText())
assert.Equal(t, "4[a=4[4d1=1+1+1+1]=4]", vm.GetDetailText())
// assert.Equal(t, "4[a=4d1=4]", vm.GetDetailText())
}
}

Expand All @@ -1773,7 +1774,8 @@ func TestDetailTextComputed2(t *testing.T) {
vm.Attrs.Store("a", NewComputedVal("4d(1d1)"))
err := vm.Run("a")
if assert.NoError(t, err) {
assert.Equal(t, "4[a=4d(1d1)=4]", vm.GetDetailText())
assert.Equal(t, "4[a=4[4d(1d1)=1+1+1+1,1d1=1]=4]", vm.GetDetailText())
// assert.Equal(t, "4[a=4d(1d1)=4]", vm.GetDetailText())
}
}

Expand Down
16 changes: 14 additions & 2 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import (
)

type VMValueType int
type IntType int // :IntType
type IntType int // :IntType
const IntTypeSize = 4 // 只能为 4 或 8(32位/64位)

const (
VMTypeInt VMValueType = 0
Expand Down Expand Up @@ -159,6 +160,8 @@ type Context struct {
IsRunning bool // 是否正在运行,Run时会置为true,halt时会置为false
CustomDiceInfo []*customDiceItem

forceSolveDetail bool // 一个辅助属性,用于computed时强制获取计算过程

/** 全局变量 */
globalNames *ValueMap

Expand Down Expand Up @@ -1406,6 +1409,7 @@ func (v *VMValue) ComputedExecute(ctx *Context, detail *BufferSpan) *VMValue {
vm.NumOpCount = ctx.NumOpCount + 100
ctx.NumOpCount = vm.NumOpCount // 防止无限递归
vm.RandSrc = ctx.RandSrc
vm.forceSolveDetail = true
if ctx.Config.OpCountLimit > 0 && vm.NumOpCount > vm.Config.OpCountLimit {
vm.Error = errors.New("允许算力上限")
ctx.Error = vm.Error
Expand All @@ -1428,8 +1432,15 @@ func (v *VMValue) ComputedExecute(ctx *Context, detail *BufferSpan) *VMValue {
}

var ret *VMValue
var detailText string
if vm.top != 0 {
ret = vm.stack[vm.top-1].Clone()
if vm.parser == nil {
// 兼容: 如果没有parser填充一个避免报错,不过会占用一些额外的内存
vm.parser = &parser{data: []byte(cd.Expr)}
vm.parser.pt.offset = len(vm.parser.data)
}
detailText = vm.makeDetailStr(vm.DetailSpans)
} else {
ret = NewNullVal()
}
Expand All @@ -1438,8 +1449,9 @@ func (v *VMValue) ComputedExecute(ctx *Context, detail *BufferSpan) *VMValue {
ctx.IsComputedLoaded = true

if detail != nil {
// detail.Expr = cd.Expr
detail.Tag = "load.computed"
detail.Text = cd.Expr
detail.Text = detailText
}
return ret
}
Expand Down

0 comments on commit e4d40be

Please sign in to comment.