Last active
October 6, 2017 22:27
-
-
Save ohga/857783a36b0f2928d06e9991c8951151 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"encoding/json" | |
"fmt" | |
"io" | |
"io/ioutil" | |
"log" | |
"os" | |
"os/exec" | |
"strconv" | |
"strings" | |
) | |
type WaffleWrapper struct { | |
Engine string `json:"engine"` | |
FadeoutCommand string `json:"fadeout_command"` | |
LogFile string `json:"logfile"` | |
TimeThreshold int `json:"time_threshold"` | |
TimeToOverwrite int `json:"time_to_overwrite"` | |
MaxScore int `json:"max_score"` | |
ChallengeChangeScore int `json:"challenge_change_score"` | |
Distance int `json:"distance"` | |
TimeToOverwrite2 int `json:"time_to_overwrite2"` | |
CancelScoreUpperLimit int `json:"cancel_score_upperlimit"` | |
CancelScoreLowerLimit int `json:"cancel_score_lowerlimit"` | |
Score int | |
Depth int | |
Nodes int | |
OldScore int | |
OldDepth int | |
OldNodes int | |
OldScore2 int | |
OldDepth2 int | |
OldNodes2 int | |
IsBlack bool | |
IsFadeoutExecuted bool | |
HasPonder bool | |
Rollback bool | |
Btime int | |
Wtime int | |
Binc int | |
Winc int | |
Byoyomi int | |
} | |
func (param *WaffleWrapper) Clear() { | |
param.Score = 0 | |
param.Depth = 0 | |
param.Nodes = 0 | |
param.OldScore = 0 | |
param.OldDepth = 0 | |
param.OldNodes = 0 | |
param.OldScore2 = 0 | |
param.OldDepth2 = 0 | |
param.OldNodes2 = 0 | |
param.IsBlack = false | |
param.IsFadeoutExecuted = false | |
param.HasPonder = false | |
param.Rollback = false | |
param.Btime = 0 | |
param.Wtime = 0 | |
param.Binc = 0 | |
param.Winc = 0 | |
param.Byoyomi = 0 | |
} | |
func (param *WaffleWrapper) Load(filename string) { | |
raw, err := ioutil.ReadFile(filename) | |
if err != nil { | |
panic(err) | |
} | |
json.Unmarshal(raw, param) | |
} | |
func (param *WaffleWrapper) Build() { | |
// 持ち時間が無い or mate が出てる。 | |
if (param.IsBlack && param.Btime < param.TimeToOverwrite2) || | |
(!param.IsBlack && param.Wtime < param.TimeToOverwrite2) || | |
param.IsFadeoutExecuted { | |
log.Printf("cancel: Time < %d or fadeout executed .\n", param.TimeToOverwrite2) | |
return | |
} | |
// スコアが上限を超えてるならどうでもいい。 | |
if param.Score > param.MaxScore { | |
log.Printf("cancel: Score(%d) > %d.\n", param.Score, param.MaxScore) | |
return | |
} | |
// 考えどころの評価値。ただし局面数や深さが一手前より増えてるならいいや。 | |
if (param.Score < param.CancelScoreUpperLimit && param.Score > param.CancelScoreLowerLimit) && | |
(param.Nodes < param.OldNodes || param.Depth < param.OldDepth) { | |
log.Printf("cancel: Score %d < %d < %d .\n", param.CancelScoreLowerLimit, param.Score, param.CancelScoreUpperLimit) | |
log.Printf(" : Depth %d < %d .\n", param.Depth, param.OldDepth) | |
log.Printf(" : Nodes %d < %d .\n", param.Nodes, param.OldNodes) | |
return | |
} | |
// 一手前との比較。増加量が閾値を超えてるなら時間攻め | |
tmp := param.Score - param.OldScore | |
if tmp > param.ChallengeChangeScore { | |
log.Printf("attack: (%d - %d) > %d.\n", param.Score, param.OldScore, param.ChallengeChangeScore) | |
if param.IsBlack { | |
param.Btime = param.TimeToOverwrite | |
} else { | |
param.Wtime = param.TimeToOverwrite | |
} | |
return | |
} | |
// https://github.com/32hiko/HoneyWaffleWCSC27/commit/31c39879f4e89af1e1b2326062b71200c72f6770#diff-8b54bb62d2105fd50238d34fba61c236 | |
if param.IsBlack { | |
if (param.Btime > param.Wtime) && (param.TimeThreshold > param.Wtime) { | |
param.Btime = param.TimeToOverwrite | |
} | |
if (param.Wtime - param.Btime) > param.Distance { | |
param.Btime = param.TimeToOverwrite2 | |
} | |
} else { | |
if (param.Wtime > param.Btime) && (param.TimeThreshold > param.Btime) { | |
param.Wtime = param.TimeToOverwrite | |
} | |
if (param.Btime - param.Wtime) > param.Distance { | |
param.Wtime = param.TimeToOverwrite2 | |
} | |
} | |
} | |
func (param *WaffleWrapper) SetGoCommand(text string) { | |
param.HasPonder = false | |
param.Btime = 0 | |
param.Wtime = 0 | |
param.Binc = -1 | |
param.Winc = -1 | |
param.Byoyomi = -1 | |
tmp := "" | |
arr := strings.Split(text, " ") | |
for _, row := range arr { | |
if row == "ponder" { | |
param.HasPonder = true | |
continue | |
} | |
nn, err := strconv.Atoi(row) | |
if err != nil { | |
tmp = row | |
} | |
if nn == 0 { | |
continue | |
} | |
switch tmp { | |
case "btime": | |
param.Btime = nn | |
case "wtime": | |
param.Wtime = nn | |
case "binc": | |
param.Binc = nn | |
case "winc": | |
param.Winc = nn | |
case "byoyomi": | |
param.Byoyomi = nn | |
} | |
nn = 0 | |
} | |
} | |
func (param *WaffleWrapper) SetGoCommandFromInfo(text string) { | |
tmp := "" | |
arr := strings.Split(text, " ") | |
for _, row := range arr { | |
nn, err := strconv.Atoi(row) | |
if err != nil { | |
tmp = row | |
} | |
if nn == 0 { | |
continue | |
} | |
switch tmp { | |
case "cp": | |
param.Score = nn | |
case "depth": | |
param.Depth = nn | |
case "nodes": | |
param.Nodes = nn | |
case "mate": | |
log.Printf(" >>: mate %d\n", nn) | |
if !param.IsFadeoutExecuted { | |
param.IsFadeoutExecuted = true | |
go param.ExecuteFadeoutCommand() | |
} | |
} | |
nn = 0 | |
} | |
} | |
func (param *WaffleWrapper) SplitCommandString(text string) []string { | |
var cmds []string | |
var tmp string | |
var quote bool = false | |
var escape bool = false | |
for _, chr := range text { | |
switch chr { | |
case '\\': | |
if escape { | |
escape = false | |
tmp = tmp + string(chr) | |
continue | |
} | |
escape = true | |
case '"': | |
if escape { | |
escape = false | |
tmp = tmp + string(chr) | |
continue | |
} | |
quote = !quote | |
case ' ': | |
if quote { | |
tmp = tmp + string(chr) | |
continue | |
} | |
cmds = append(cmds, tmp) | |
tmp = "" | |
default: | |
tmp = tmp + string(chr) | |
} | |
} | |
if len(tmp) != 0 { | |
cmds = append(cmds, tmp) | |
} | |
return cmds | |
} | |
func (param *WaffleWrapper) ExecuteFadeoutCommand() { | |
log.Printf(" >>: exec %s\n", param.FadeoutCommand) | |
// cmds := strings.Split(param.FadeoutCommand, " ") | |
cmds := param.SplitCommandString(param.FadeoutCommand) | |
var cmd *exec.Cmd | |
if len(cmds) == 1 { | |
cmd = exec.Command(cmds[0]) | |
} else { | |
cmd = exec.Command(cmds[0], cmds[1:]...) | |
} | |
if err := cmd.Start(); err != nil { | |
log.Println(err) | |
} | |
} | |
func (param *WaffleWrapper) GetGoCommand() string { | |
var rtn = "go" | |
if param.HasPonder { | |
rtn = fmt.Sprintf("%s ponder", rtn) | |
} | |
if param.Btime >= 0 { | |
rtn = fmt.Sprintf("%s btime %d", rtn, param.Btime) | |
} | |
if param.Wtime >= 0 { | |
rtn = fmt.Sprintf("%s wtime %d", rtn, param.Wtime) | |
} | |
if param.Byoyomi >= 0 { | |
rtn = fmt.Sprintf("%s byoyomi %d", rtn, param.Byoyomi) | |
} | |
if param.Binc >= 0 { | |
rtn = fmt.Sprintf("%s binc %d", rtn, param.Binc) | |
} | |
if param.Winc >= 0 { | |
rtn = fmt.Sprintf("%s winc %d", rtn, param.Winc) | |
} | |
return rtn | |
} | |
func (param *WaffleWrapper) FromCommandToStdout(scanner *bufio.Scanner) { | |
for scanner.Scan() { | |
line := scanner.Text() | |
if strings.HasPrefix(line, "info") { | |
param.SetGoCommandFromInfo(line) | |
//log.Printf("<-----: info cp %d(%d) depth %d nodes %d.\n", param.Score, param.OldScore, param.Depth, param.Nodes) | |
} else if strings.HasPrefix(line, "bestmove") { | |
log.Printf("<-----: %s.\n", line) | |
if param.Rollback { | |
log.Printf("----->: rollback.\n") | |
param.Score = param.OldScore | |
param.Depth = param.OldDepth | |
param.Nodes = param.OldNodes | |
param.OldScore = param.OldScore2 | |
param.OldDepth = param.OldDepth2 | |
param.OldNodes = param.OldNodes2 | |
} | |
} else { | |
log.Printf("<-----: %s.\n", line) | |
} | |
fmt.Printf("%s\n", line) | |
} | |
} | |
func (param *WaffleWrapper) FromQueToCommand(ch chan string, stdin io.WriteCloser) { | |
for { | |
text, ok := <-ch | |
if !ok { | |
return | |
} | |
if !strings.HasPrefix(text, "position") { | |
log.Printf("----->: %s.\n", text) | |
} else { | |
arr := strings.Split(text, " ") | |
if len(arr) > 4 { | |
log.Printf("----->: position (%d) .. %s %s.\n", len(arr)-4, arr[len(arr)-2], arr[len(arr)-1]) | |
} | |
} | |
if strings.HasPrefix(text, "go") { | |
param.SetGoCommand(text) | |
param.Rollback = false | |
param.Build() | |
text = param.GetGoCommand() | |
log.Printf(" >>: %s.(%d .. %d)\n", text, param.OldScore, param.Score) | |
param.OldScore2 = param.OldScore | |
param.OldDepth2 = param.OldDepth | |
param.OldNodes2 = param.OldNodes | |
param.OldScore = param.Score | |
param.OldDepth = param.Depth | |
param.OldNodes = param.Nodes | |
} else if strings.HasPrefix(text, "stop") { | |
param.Rollback = true | |
} | |
fmt.Fprintf(stdin, "%s\n", text) | |
if text == "quit" { | |
break | |
} | |
} | |
log.Printf("end wrapper.\n") | |
os.Exit(0) | |
} | |
func (param *WaffleWrapper) FromStdinToQue(reader *bufio.Reader, ch chan string) { | |
var isNoColor = true | |
for { | |
text, _, err := reader.ReadLine() | |
if err == io.EOF { | |
break | |
} else if err != nil { | |
panic(err) | |
} | |
if strings.HasPrefix(string(text), "wf_fadeout") { | |
go param.ExecuteFadeoutCommand() | |
continue | |
} | |
if strings.HasPrefix(string(text), "isready") { | |
isNoColor = true | |
} | |
if isNoColor { | |
param.Clear() | |
if strings.HasPrefix(string(text), "position") { | |
// 先手なら"position startpos"だけ | |
tmpArr := strings.Split(string(text), " ") | |
if len(tmpArr) == 2 { | |
param.IsBlack = true | |
log.Printf("!!! black.\n") | |
} else { | |
param.IsBlack = false | |
log.Printf("!!! white.\n") | |
} | |
isNoColor = false | |
} | |
} | |
ch <- string(text) | |
} | |
} | |
func main() { | |
param := WaffleWrapper{} | |
param.Load("settings.json") | |
if len(param.Engine) == 0 { | |
fmt.Println("load error(settings.json)") | |
os.Exit(1) | |
} | |
if len(param.LogFile) == 0 { | |
log.SetFlags(0) | |
log.SetOutput(ioutil.Discard) | |
} else { | |
logfile, err := os.OpenFile(param.LogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) | |
if err != nil { | |
panic(err) | |
} | |
log.SetFlags(log.Ldate | log.Ltime) | |
log.SetOutput(logfile) | |
} | |
log.Printf("start wrapper.\n") | |
cmds := param.SplitCommandString(param.Engine) | |
var subProcess *exec.Cmd | |
if len(cmds) == 1 { | |
subProcess = exec.Command(cmds[0]) | |
} else { | |
subProcess = exec.Command(cmds[0], cmds[1:]...) | |
} | |
subProcess.Stderr = os.Stderr | |
stdin, err := subProcess.StdinPipe() | |
if err != nil { | |
panic(err) | |
} | |
defer stdin.Close() | |
stdout, err := subProcess.StdoutPipe() | |
if err != nil { | |
panic(err) | |
} | |
defer stdout.Close() | |
if err = subProcess.Start(); err != nil { | |
panic(err) | |
} | |
ch := make(chan string) | |
scanner := bufio.NewScanner(stdout) | |
reader := bufio.NewReader(os.Stdin) | |
go param.FromCommandToStdout(scanner) | |
go param.FromQueToCommand(ch, stdin) | |
param.FromStdinToQue(reader, ch) | |
log.Printf("end wrapper.\n") | |
} | |
/* example settings.json {{{ | |
{ | |
"engine": "YaneuraOu-by-gcc", | |
"fadeout_command": "cmd.exe /c start getwild.mp3", | |
"logfile": "wf_debug.log", | |
"time_threshold": 60000, | |
"time_to_overwrite": 8000, | |
"max_score": 1000, | |
"challenge_change_score": 500, | |
"distance": 120000, | |
"time_to_overwrite2": 15000, | |
"cancel_score_upperlimit": 100, | |
"cancel_score_lowerlimit": -500, | |
"_comment": [ | |
"engine // USI Engine", | |
"fadeout_command // mate を受けた時に実行するコマンド。(省略時はなにもしない)", | |
"logfile // ログファイル(省略時はoff)", | |
"// 時間攻めの設定", | |
"time_threshold = 60 * 1000 // 時間攻めを開始する相手の残り持ち時間(msec)", | |
"time_to_overwrite = 8 * 1000 // 時間攻め応手にかける時間(msec)", | |
"max_score = 1000 // 発動する評価値上限、これ以上の評価値がついたら無理に時間攻めはしない", | |
"challenge_change_score = 500 // 前回の指し手の評価値との増加量を見て、無条件で時間攻めをする評価値の下限", | |
"// なるべく相手との持ち時間が離されないようにする為の設定", | |
"distance = 120 * 1000 // 持ち時間の差(msec)", | |
"time_to_overwrite2 = 15 * 1000 // 応手にかける時間(msec)", | |
"// 考えどころの評価値の範囲の設定。(のつもり)", | |
"// 時間攻めや、なるべく離されないようにする、の持ち時間操作をキャンセルする。", | |
"// ただし、前回の指し手の局面数や深さが増えていたらキャンセルされない。(十分考えている、はず?)", | |
"cancel_score_upperlimit = -100 // 評価値範囲上限", | |
"cancel_score_lowerlimit = -500 // 評価値範囲下限" | |
] | |
} | |
}}} */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment