igoui

Log | Files | Refs

main.go (5935B)


      1 package main
      2 
      3 import (
      4 	"image"
      5 	"image/color"
      6 	"strings"
      7 	// "image/png"
      8 	"fmt"
      9 	"log"
     10 	"os"
     11 
     12 	// UI generation
     13 	"gioui.org/app"
     14 	"gioui.org/f32"
     15 	"gioui.org/io/event"
     16 	"gioui.org/io/key"
     17 	"gioui.org/io/pointer"
     18 	"gioui.org/layout"
     19 	"gioui.org/op"
     20 	"gioui.org/op/clip"
     21 	"gioui.org/op/paint"
     22 	"gioui.org/unit"
     23 	"gioui.org/widget"
     24 	"gioui.org/widget/material"
     25 	// "gioui.org/text"
     26 	// "gioui.org/widget/material"
     27 
     28 	// SGF manipulation
     29 	"github.com/rooklift/sgf"
     30 )
     31 
     32 type App struct {
     33 	Node   *sgf.Node
     34 	Katago *Katago
     35 	Editor material.EditorStyle
     36 }
     37 
     38 func (g *App) SetNode(node *sgf.Node) {
     39 	if g.Node != nil {
     40 		newcomment := g.Editor.Editor.Text()
     41 		_, present := g.Node.GetValue("C")
     42 		if newcomment != "" || present {
     43 			g.Node.SetValue("C", newcomment)
     44 		}
     45 	}
     46 	g.Node = node
     47 
     48 	val, ok := node.GetValue("C")
     49 	if ok {
     50 		g.Editor.Editor.SetText(val)
     51 	} else {
     52 		g.Editor.Editor.SetText("")
     53 	}
     54 
     55 	go g.AnalyzeCurrent()
     56 }
     57 
     58 func (g *App) AnalyzeCurrent() {
     59 	root := g.Node.GetRoot()
     60 	board := root.Board()
     61 	rules, _ := root.GetValue("RU")
     62 
     63 	line := g.Node.GetLine()
     64 
     65 	moves := make([][]string, 0)
     66 	for _, inner := range line {
     67 		move, iswhite := inner.GetValue("W")
     68 		var color string
     69 		var isblack bool
     70 		if iswhite {
     71 			color = "W"
     72 		} else if move, isblack = inner.GetValue("B"); isblack {
     73 			color = "B"
     74 		}
     75 
     76 		if iswhite || isblack {
     77 			x, y, _ := sgf.ParsePoint(move, board.Size)
     78 			moves = append(moves, []string{color, fmt.Sprintf("(%d,%d)", x, y)})
     79 		}
     80 	}
     81 
     82 	initialStones := make([][]string, 0)
     83 
     84 	_, present := root.GetValue("HA")
     85 	if present {
     86 		log.Fatal("TODO")
     87 	}
     88 
     89 	request := Request{
     90 		Komi:          root.RootKomi(),
     91 		Rules:         strings.ToLower(rules),
     92 		XSize:         board.Size,
     93 		YSize:         board.Size,
     94 		Moves:         moves,
     95 		InitialStones: initialStones,
     96 	}
     97 
     98 	respch, err := g.Katago.request(request)
     99 	if err != nil {
    100 		log.Fatal(err)
    101 	}
    102 
    103 	log.Print("Waiting for response")
    104 	resp := <-respch
    105 	log.Print("Got response ", resp)
    106 }
    107 
    108 // Draws the goban in the area
    109 // Also handles navigation of the game tree
    110 func (g *App) LayoutGoban(gtx layout.Context) layout.Dimensions {
    111 	size := min(gtx.Constraints.Max.X, gtx.Constraints.Max.Y)
    112 	const padding = 2
    113 	black := color.NRGBA{A: 0xFF}
    114 	white := color.NRGBA{A: 0xFF, R: 0xFF, G: 0xFF, B: 0xFF}
    115 
    116 	// Handle inputs within the goban area
    117 	{
    118 		defer clip.Rect(image.Rect(0, 0, size, size)).Push(gtx.Ops).Pop()
    119 		offset := float32(size) / float32(g.Node.Board().Size)
    120 
    121 		event.Op(gtx.Ops, g)
    122 
    123 		for {
    124 			ev, ok := gtx.Event(pointer.Filter{
    125 				Target: g,
    126 				Kinds:  pointer.Press,
    127 			}, key.Filter{
    128 				Name: key.NameDownArrow,
    129 			}, key.Filter{
    130 				Name: key.NameUpArrow,
    131 			})
    132 			if !ok {
    133 				break
    134 			}
    135 
    136 			var newnode *sgf.Node
    137 			switch ev.(type) {
    138 			case pointer.Event:
    139 				{
    140 					e := ev.(pointer.Event)
    141 					switch e.Kind {
    142 					case pointer.Press:
    143 						xpos := int(e.Position.X / offset)
    144 						ypos := int(e.Position.Y / offset)
    145 						var err error
    146 						newnode, err = g.Node.Play(sgf.Point(xpos, ypos))
    147 						if err != nil {
    148 							newnode = nil
    149 						}
    150 						newnode.MakeMainLine()
    151 					}
    152 				}
    153 			case key.Event:
    154 				{
    155 					e := ev.(key.Event)
    156 					if e.State == key.Press {
    157 						switch e.Name {
    158 						case key.NameDownArrow:
    159 							newnode = g.Node.MainChild()
    160 						case key.NameUpArrow:
    161 							newnode = g.Node.Parent()
    162 						}
    163 					}
    164 				}
    165 			}
    166 			if newnode != nil {
    167 				g.SetNode(newnode)
    168 			}
    169 		}
    170 	}
    171 
    172 	board := g.Node.Board()
    173 	offset := float32(size) / float32(board.Size)
    174 
    175 	// Draw the frame
    176 	var path clip.Path
    177 	path.Begin(gtx.Ops)
    178 	foffset := offset / 2
    179 	for i := 0; i < board.Size; i++ {
    180 		ioffset := float32(offset)*float32(i) + foffset
    181 		// Line from left to right
    182 		path.MoveTo(f32.Pt(ioffset, foffset))
    183 		path.LineTo(f32.Pt(ioffset, float32(size)-foffset))
    184 
    185 		// Line from top to bottom
    186 		path.MoveTo(f32.Pt(foffset, ioffset))
    187 		path.LineTo(f32.Pt(float32(size)-foffset, ioffset))
    188 	}
    189 	path.Close()
    190 	paint.FillShape(gtx.Ops, black, clip.Stroke{
    191 		Path:  path.End(),
    192 		Width: 4,
    193 	}.Op())
    194 
    195 	// Now draw the stones
    196 	// TODO: images ?
    197 	for x := 0; x < board.Size; x++ {
    198 		for y := 0; y < board.Size; y++ {
    199 			if board.State[x][y] != sgf.EMPTY {
    200 				scolor := board.State[x][y]
    201 				fx := float32(x)
    202 				fy := float32(y)
    203 				rect := image.Rect(int(fx*offset+padding/2), int(fy*offset+padding/2), int((fx+1)*offset-padding/2), int((fy+1)*offset-padding/2))
    204 				elipse := clip.Ellipse(rect)
    205 
    206 				var strokecolor color.NRGBA
    207 				var fillcolor color.NRGBA
    208 
    209 				if scolor == sgf.BLACK {
    210 					strokecolor = white
    211 					fillcolor = black
    212 				} else {
    213 					strokecolor = black
    214 					fillcolor = white
    215 				}
    216 
    217 				// Now that we have the rectangle, put the stone
    218 				paint.FillShape(gtx.Ops, fillcolor, elipse.Op(gtx.Ops))
    219 				paint.FillShape(gtx.Ops, strokecolor, clip.Stroke{
    220 					Path:  elipse.Path(gtx.Ops),
    221 					Width: padding,
    222 				}.Op())
    223 			}
    224 		}
    225 	}
    226 
    227 	return layout.Dimensions{Size: image.Pt(size, size)}
    228 }
    229 
    230 func run(window *app.Window, goban *App) error {
    231 	var ops op.Ops
    232 
    233 	for {
    234 		switch e := window.Event().(type) {
    235 		case app.DestroyEvent:
    236 			return e.Err
    237 		case app.FrameEvent:
    238 			// This graphics context is used for managing the rendering state.
    239 			gtx := app.NewContext(&ops, e)
    240 
    241 			layout.Flex{}.Layout(gtx,
    242 				layout.Rigid(func(gtx layout.Context) layout.Dimensions {
    243 					return layout.UniformInset(unit.Dp(30)).Layout(gtx, goban.LayoutGoban)
    244 				}),
    245 				layout.Flexed(0.5, goban.Editor.Layout),
    246 			)
    247 
    248 			// Pass the drawing operations to the GPU.
    249 			e.Frame(gtx.Ops)
    250 		}
    251 	}
    252 }
    253 
    254 func main() {
    255 	th := material.NewTheme()
    256 	goban := new(App)
    257 
    258 	// Start KataGo
    259 	kat, err := newKatago()
    260 	if err != nil {
    261 		log.Fatal(err)
    262 	}
    263 	goban.Katago = kat
    264 
    265 	goban.Editor = material.Editor(th, new(widget.Editor), "")
    266 	goban.SetNode(sgf.LoadArgOrQuit(1))
    267 
    268 	go func() {
    269 		window := new(app.Window)
    270 		window.Option(app.MinSize(unit.Dp(1000), unit.Dp(700)))
    271 		err := run(window, goban)
    272 		if err != nil {
    273 			log.Fatal(err)
    274 		}
    275 		os.Exit(0)
    276 	}()
    277 
    278 	app.Main()
    279 }