9fans.net/go@v0.0.5/games/4s/xs.go (about)

     1  // games/4s - a tetris clone
     2  //
     3  // Derived from Plan 9's /sys/src/games/xs.c
     4  // http://plan9.bell-labs.com/sources/plan9/sys/src/games/xs.c
     5  
     6  /*
     7   * engine for 4s, 5s, etc
     8   */
     9  
    10  package main
    11  
    12  import (
    13  	"fmt"
    14  	"image"
    15  	"log"
    16  	"math/rand"
    17  	"os"
    18  	"time"
    19  
    20  	"9fans.net/go/draw"
    21  )
    22  
    23  /*
    24  Cursor whitearrow = {
    25  	{0, 0},
    26  	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
    27  	 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC,
    28  	 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
    29  	 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
    30  	{0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C,
    31  	 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C,
    32  	 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C,
    33  	 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
    34  };
    35  */
    36  
    37  const (
    38  	CNone   = 0
    39  	CBounds = 1
    40  	CPiece  = 2
    41  	NX      = 10
    42  	NY      = 20
    43  
    44  	NCOL = 10
    45  
    46  	MAXN = 5
    47  )
    48  
    49  var (
    50  	N       int
    51  	display *draw.Display
    52  	screen  *draw.Image
    53  	screenr image.Rectangle
    54  
    55  	board                    [NY][NX]byte
    56  	rboard                   image.Rectangle
    57  	pscore                   image.Point
    58  	scoresz                  image.Point
    59  	pcsz                     = 32
    60  	pos                      image.Point
    61  	bb, bbmask, bb2, bb2mask *draw.Image
    62  	whitemask                *draw.Image
    63  	br, br2                  image.Rectangle
    64  	points                   int
    65  	dt                       int
    66  	DY                       int
    67  	DMOUSE                   int
    68  	lastmx                   int
    69  	mouse                    draw.Mouse
    70  	newscreen                bool
    71  	timerc                   <-chan time.Time
    72  	suspc                    chan bool
    73  	mousec                   chan draw.Mouse
    74  	kbdc                     chan rune
    75  	mousectl                 *draw.Mousectl
    76  	kbdctl                   *draw.Keyboardctl
    77  	suspended                bool
    78  	tsleep                   int
    79  	piece                    *Piece
    80  	pieces                   []Piece
    81  )
    82  
    83  type Piece struct {
    84  	rot   int
    85  	tx    int
    86  	sz    image.Point
    87  	d     []image.Point
    88  	left  *Piece
    89  	right *Piece
    90  }
    91  
    92  var txbits = [NCOL][32]byte{
    93  	[32]byte{0xDD, 0xDD, 0xFF, 0xFF, 0x77, 0x77, 0xFF, 0xFF,
    94  		0xDD, 0xDD, 0xFF, 0xFF, 0x77, 0x77, 0xFF, 0xFF,
    95  		0xDD, 0xDD, 0xFF, 0xFF, 0x77, 0x77, 0xFF, 0xFF,
    96  		0xDD, 0xDD, 0xFF, 0xFF, 0x77, 0x77, 0xFF, 0xFF},
    97  	[32]byte{0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77,
    98  		0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77,
    99  		0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77,
   100  		0xDD, 0xDD, 0x77, 0x77, 0xDD, 0xDD, 0x77, 0x77},
   101  	[32]byte{0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55,
   102  		0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55,
   103  		0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55,
   104  		0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55},
   105  	[32]byte{0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55,
   106  		0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55,
   107  		0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55,
   108  		0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55},
   109  	[32]byte{0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88,
   110  		0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88,
   111  		0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88,
   112  		0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88},
   113  	[32]byte{0x22, 0x22, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00,
   114  		0x22, 0x22, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00,
   115  		0x22, 0x22, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00,
   116  		0x22, 0x22, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00},
   117  	[32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
   118  		0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
   119  		0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
   120  		0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
   121  	[32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
   122  		0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
   123  		0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
   124  		0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
   125  	[32]byte{0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
   126  		0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
   127  		0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
   128  		0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC},
   129  	[32]byte{0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33,
   130  		0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33,
   131  		0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33,
   132  		0xCC, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0x33, 0x33},
   133  }
   134  
   135  var txpix = [NCOL]draw.Color{
   136  	draw.Yellow,    /* yellow */
   137  	draw.Cyan,      /* cyan */
   138  	draw.Green,     /* lime green */
   139  	draw.GreyBlue,  /* slate */
   140  	draw.Red,       /* red */
   141  	draw.GreyGreen, /* olive green */
   142  	draw.Blue,      /* blue */
   143  	0xFF55AAFF,     /* pink */
   144  	0xFFAAFFFF,     /* lavender */
   145  	0xBB005DFF,     /* maroon */
   146  }
   147  
   148  var tx [NCOL]*draw.Image
   149  
   150  func movemouse() int {
   151  	mouse.Point = image.Pt(rboard.Min.X+rboard.Dx()/2, rboard.Min.Y+rboard.Dy()/2)
   152  	//mousectl.MoveTo(mouse.Point)
   153  	return mouse.X
   154  }
   155  
   156  func warp(p image.Point, x int) int {
   157  	if !suspended && piece != nil {
   158  		x = pos.X + piece.sz.X*pcsz/2
   159  		if p.Y < rboard.Min.Y {
   160  			p.Y = rboard.Min.Y
   161  		}
   162  		if p.Y >= rboard.Max.Y {
   163  			p.Y = rboard.Max.Y - 1
   164  		}
   165  		//mousectl.MoveTo(image.Pt(x, p.Y))
   166  	}
   167  	return x
   168  }
   169  
   170  func initPieces() {
   171  	for i := range pieces {
   172  		p := &pieces[i]
   173  		if p.rot == 3 {
   174  			p.right = &pieces[i-3]
   175  		} else {
   176  			p.right = &pieces[i+1]
   177  		}
   178  		if p.rot == 0 {
   179  			p.left = &pieces[i+3]
   180  		} else {
   181  			p.left = &pieces[i-1]
   182  		}
   183  	}
   184  }
   185  
   186  func collide(pt image.Point, p *Piece) bool {
   187  	pt.X = (pt.X - rboard.Min.X) / pcsz
   188  	pt.Y = (pt.Y - rboard.Min.Y) / pcsz
   189  	for _, q := range p.d {
   190  		pt.X += q.X
   191  		pt.Y += q.Y
   192  		if pt.X < 0 || pt.X >= NX || pt.Y < 0 || pt.Y >= NY {
   193  			return true
   194  		}
   195  		if board[pt.Y][pt.X] != 0 {
   196  			return true
   197  		}
   198  	}
   199  	return false
   200  }
   201  
   202  func collider(pt, pmax image.Point) bool {
   203  	pi := (pt.X - rboard.Min.X) / pcsz
   204  	pj := (pt.Y - rboard.Min.Y) / pcsz
   205  	n := pmax.X / pcsz
   206  	m := pmax.Y/pcsz + 1
   207  	for i := pi; i < pi+n && i < NX; i++ {
   208  		for j := pj; j < pj+m && j < NY; j++ {
   209  			if board[j][i] != 0 {
   210  				return true
   211  			}
   212  		}
   213  	}
   214  	return false
   215  }
   216  
   217  func setpiece(p *Piece) {
   218  	bb.Draw(bb.R, display.White, nil, draw.ZP)
   219  	bbmask.Draw(bb.R, display.Transparent, nil, draw.ZP)
   220  	br = image.Rect(0, 0, 0, 0)
   221  	br2 = br
   222  	piece = p
   223  	if p == nil {
   224  		return
   225  	}
   226  	var op image.Point
   227  	var r image.Rectangle
   228  	r.Min = bb.R.Min
   229  	for i, pt := range p.d {
   230  		r.Min.X += pt.X * pcsz
   231  		r.Min.Y += pt.Y * pcsz
   232  		r.Max.X = r.Min.X + pcsz
   233  		r.Max.Y = r.Min.Y + pcsz
   234  		if i == 0 {
   235  			bb.Draw(r, display.Black, nil, draw.ZP)
   236  			bb.Draw(r.Inset(1), tx[piece.tx], nil, draw.ZP)
   237  			bbmask.Draw(r, display.Opaque, nil, draw.ZP)
   238  			op = r.Min
   239  		} else {
   240  			bb.Draw(r, bb, nil, op)
   241  			bbmask.Draw(r, bbmask, nil, op)
   242  		}
   243  		if br.Max.X < r.Max.X {
   244  			br.Max.X = r.Max.X
   245  		}
   246  		if br.Max.Y < r.Max.Y {
   247  			br.Max.Y = r.Max.Y
   248  		}
   249  	}
   250  	br.Max = br.Max.Sub(bb.R.Min)
   251  	delta := image.Pt(0, DY)
   252  	br2.Max = br.Max.Add(delta)
   253  	r = br.Add(bb2.R.Min)
   254  	r2 := br2.Add(bb2.R.Min)
   255  	bb2.Draw(r2, display.White, nil, draw.ZP)
   256  	bb2.Draw(r.Add(delta), bb, nil, bb.R.Min)
   257  	bb2mask.Draw(r2, display.Transparent, nil, draw.ZP)
   258  	bb2mask.Draw(r, display.Opaque, bbmask, bb.R.Min)
   259  	bb2mask.Draw(r.Add(delta), display.Opaque, bbmask, bb.R.Min)
   260  }
   261  
   262  func drawpiece() {
   263  	screen.Draw(br.Add(pos), bb, bbmask, bb.R.Min)
   264  	if suspended {
   265  		screen.Draw(br.Add(pos), display.White, whitemask, draw.ZP)
   266  	}
   267  }
   268  
   269  func undrawpiece() {
   270  	var mask *draw.Image
   271  	if collider(pos, br.Max) {
   272  		mask = bbmask
   273  	}
   274  	screen.Draw(br.Add(pos), display.White, mask, bb.R.Min)
   275  }
   276  
   277  func rest() {
   278  	pt := pos.Sub(rboard.Min).Div(pcsz)
   279  	for _, p := range piece.d {
   280  		pt.X += p.X
   281  		pt.Y += p.Y
   282  		board[pt.Y][pt.X] = byte(piece.tx + 16)
   283  	}
   284  }
   285  
   286  func canfit(p *Piece) bool {
   287  	var dx = [...]int{0, -1, 1, -2, 2, -3, 3, 4, -4}
   288  	j := N + 1
   289  	if j >= 4 {
   290  		j = p.sz.X
   291  		if j < p.sz.Y {
   292  			j = p.sz.Y
   293  		}
   294  		j = 2*j - 1
   295  	}
   296  	for i := 0; i < j; i++ {
   297  		var z image.Point
   298  		z.X = pos.X + dx[i]*pcsz
   299  		z.Y = pos.Y
   300  		if !collide(z, p) {
   301  			z.Y = pos.Y + pcsz - 1
   302  			if !collide(z, p) {
   303  				undrawpiece()
   304  				pos.X = z.X
   305  				return true
   306  			}
   307  		}
   308  	}
   309  	return false
   310  }
   311  
   312  func score(p int) {
   313  	points += p
   314  	buf := fmt.Sprintf("%.6d", points)
   315  	screen.Draw(image.Rectangle{pscore, pscore.Add(scoresz)}, display.White, nil, draw.ZP)
   316  	screen.String(pscore, display.Black, draw.ZP, display.Font, buf)
   317  }
   318  
   319  func drawsq(b *draw.Image, p image.Point, ptx int) {
   320  	var r image.Rectangle
   321  	r.Min = p
   322  	r.Max.X = r.Min.X + pcsz
   323  	r.Max.Y = r.Min.Y + pcsz
   324  	b.Draw(r, display.Black, nil, draw.ZP)
   325  	b.Draw(r.Inset(1), tx[ptx], nil, draw.ZP)
   326  }
   327  
   328  func drawboard() {
   329  	screen.Border(rboard.Inset(-2), 2, display.Black, draw.ZP)
   330  	screen.Draw(image.Rect(rboard.Min.X, rboard.Min.Y-2, rboard.Max.X, rboard.Min.Y),
   331  		display.White, nil, draw.ZP)
   332  	for i := 0; i < NY; i++ {
   333  		for j := 0; j < NX; j++ {
   334  			if board[i][j] != 0 {
   335  				drawsq(screen, image.Pt(rboard.Min.X+j*pcsz, rboard.Min.Y+i*pcsz), int(board[i][j]-16))
   336  			}
   337  		}
   338  	}
   339  	score(0)
   340  	if suspended {
   341  		screen.Draw(screenr, display.White, whitemask, draw.ZP)
   342  	}
   343  }
   344  
   345  func choosepiece() {
   346  	for {
   347  		i := rand.Intn(len(pieces))
   348  		setpiece(&pieces[i])
   349  		pos = rboard.Min
   350  		pos.X += rand.Intn(NX) * pcsz
   351  		if !collide(image.Pt(pos.X, pos.Y+pcsz-DY), piece) {
   352  			break
   353  		}
   354  	}
   355  	drawpiece()
   356  	display.Flush()
   357  }
   358  
   359  func movepiece() bool {
   360  	var mask *draw.Image
   361  	if collide(image.Pt(pos.X, pos.Y+pcsz), piece) {
   362  		return false
   363  	}
   364  	if collider(pos, br2.Max) {
   365  		mask = bb2mask
   366  	}
   367  	screen.Draw(br2.Add(pos), bb2, mask, bb2.R.Min)
   368  	pos.Y += DY
   369  	display.Flush()
   370  	return true
   371  }
   372  
   373  func suspend(s bool) {
   374  	suspended = s
   375  	/*
   376  		if suspended {
   377  			setcursor(mousectl, &whitearrow);
   378  		} else {
   379  			setcursor(mousectl, nil);
   380  		}
   381  	*/
   382  	if !suspended {
   383  		drawpiece()
   384  	}
   385  	drawboard()
   386  	display.Flush()
   387  }
   388  
   389  func pause(t int) {
   390  	display.Flush()
   391  	for {
   392  		select {
   393  		case s := <-suspc:
   394  			if !suspended && s {
   395  				suspend(true)
   396  			} else if suspended && !s {
   397  				suspend(false)
   398  				lastmx = warp(mouse.Point, lastmx)
   399  			}
   400  		case <-timerc:
   401  			if suspended {
   402  				break
   403  			}
   404  			t -= tsleep
   405  			if t < 0 {
   406  				return
   407  			}
   408  		case <-mousectl.Resize:
   409  			redraw(true)
   410  		case mouse = <-mousec:
   411  		case <-kbdc:
   412  		}
   413  	}
   414  }
   415  
   416  func horiz() bool {
   417  	var lev [MAXN]int
   418  	h := 0
   419  	for i := 0; i < NY; i++ {
   420  		for j := 0; board[i][j] != 0; j++ {
   421  			if j == NX-1 {
   422  				lev[h] = i
   423  				h++
   424  				break
   425  			}
   426  		}
   427  	}
   428  	if h == 0 {
   429  		return false
   430  	}
   431  	r := rboard
   432  	newscreen = false
   433  	for j := 0; j < h; j++ {
   434  		r.Min.Y = rboard.Min.Y + lev[j]*pcsz
   435  		r.Max.Y = r.Min.Y + pcsz
   436  		screen.Draw(r, display.White, whitemask, draw.ZP)
   437  		display.Flush()
   438  	}
   439  	for i := 0; i < 3; i++ {
   440  		pause(250)
   441  		if newscreen {
   442  			drawboard()
   443  			break
   444  		}
   445  		for j := 0; j < h; j++ {
   446  			r.Min.Y = rboard.Min.Y + lev[j]*pcsz
   447  			r.Max.Y = r.Min.Y + pcsz
   448  			screen.Draw(r, display.White, whitemask, draw.ZP)
   449  		}
   450  		display.Flush()
   451  	}
   452  	r = rboard
   453  	for j := 0; j < h; j++ {
   454  		i := NY - lev[j] - 1
   455  		score(250 + 10*i*i)
   456  		r.Min.Y = rboard.Min.Y
   457  		r.Max.Y = rboard.Min.Y + lev[j]*pcsz
   458  		screen.Draw(r.Add(image.Pt(0, pcsz)), screen, nil, r.Min)
   459  		r.Max.Y = rboard.Min.Y + pcsz
   460  		screen.Draw(r, display.White, nil, draw.ZP)
   461  		for k := lev[j] - 1; k >= 0; k-- {
   462  			board[k+1] = board[k]
   463  		}
   464  		board[0] = [NX]byte{}
   465  	}
   466  	display.Flush()
   467  	return true
   468  }
   469  
   470  func mright() {
   471  	if !collide(image.Pt(pos.X+pcsz, pos.Y), piece) && !collide(image.Pt(pos.X+pcsz, pos.Y+pcsz-DY), piece) {
   472  		undrawpiece()
   473  		pos.X += pcsz
   474  		drawpiece()
   475  		display.Flush()
   476  	}
   477  }
   478  
   479  func mleft() {
   480  	if !collide(image.Pt(pos.X-pcsz, pos.Y), piece) && !collide(image.Pt(pos.X-pcsz, pos.Y+pcsz-DY), piece) {
   481  		undrawpiece()
   482  		pos.X -= pcsz
   483  		drawpiece()
   484  		display.Flush()
   485  	}
   486  }
   487  
   488  func rright() {
   489  	if canfit(piece.right) {
   490  		setpiece(piece.right)
   491  		drawpiece()
   492  		display.Flush()
   493  	}
   494  }
   495  
   496  func rleft() {
   497  	if canfit(piece.left) {
   498  		setpiece(piece.left)
   499  		drawpiece()
   500  		display.Flush()
   501  	}
   502  }
   503  
   504  var fusst = 0
   505  
   506  func drop(f bool) bool {
   507  	if f {
   508  		score(5 * (rboard.Max.Y - pos.Y) / pcsz)
   509  		for movepiece() {
   510  		}
   511  	}
   512  	fusst = 0
   513  	rest()
   514  	if pos.Y == rboard.Min.Y && !horiz() {
   515  		return true
   516  	}
   517  	horiz()
   518  	setpiece(nil)
   519  	pause(1500)
   520  	choosepiece()
   521  	lastmx = warp(mouse.Point, lastmx)
   522  	return false
   523  }
   524  
   525  func play() {
   526  	var om draw.Mouse
   527  	dt = 64
   528  	lastmx = -1
   529  	lastmx = movemouse()
   530  	choosepiece()
   531  	lastmx = warp(mouse.Point, lastmx)
   532  	for {
   533  		select {
   534  		case mouse = <-mousec:
   535  			if suspended {
   536  				om = mouse
   537  				break
   538  			}
   539  			if lastmx < 0 {
   540  				lastmx = mouse.X
   541  			}
   542  			if mouse.X > lastmx+DMOUSE {
   543  				mright()
   544  				lastmx = mouse.X
   545  			}
   546  			if mouse.X < lastmx-DMOUSE {
   547  				mleft()
   548  				lastmx = mouse.X
   549  			}
   550  			if mouse.Buttons&^om.Buttons&1 == 1 {
   551  				rleft()
   552  			}
   553  			if mouse.Buttons&^om.Buttons&2 == 2 {
   554  				if drop(true) {
   555  					return
   556  				}
   557  			}
   558  			if mouse.Buttons&^om.Buttons&4 == 4 {
   559  				rright()
   560  			}
   561  			om = mouse
   562  
   563  		case s := <-suspc:
   564  			if !suspended && s {
   565  				suspend(true)
   566  			} else if suspended && !s {
   567  				suspend(false)
   568  				lastmx = warp(mouse.Point, lastmx)
   569  			}
   570  
   571  		case <-mousectl.Resize:
   572  			redraw(true)
   573  
   574  		case r := <-kbdc:
   575  			if suspended {
   576  				break
   577  			}
   578  			switch r {
   579  			case 'f', ';':
   580  				mright()
   581  			case 'a', 'j':
   582  				mleft()
   583  			case 'd', 'l':
   584  				rright()
   585  			case 's', 'k':
   586  				rleft()
   587  			case ' ':
   588  				if drop(true) {
   589  					return
   590  				}
   591  			}
   592  
   593  		case <-timerc:
   594  			if suspended {
   595  				break
   596  			}
   597  			dt -= tsleep
   598  			if dt < 0 {
   599  				i := 1
   600  				dt = 16 * (points + rand.Intn(10000) - 5000) / 10000
   601  				if dt >= 32 {
   602  					i += (dt - 32) / 16
   603  					dt = 32
   604  				}
   605  				dt = 52 - dt
   606  				for ; i > 0; i-- {
   607  					if movepiece() {
   608  						continue
   609  					}
   610  					fusst++
   611  					if fusst == 40 {
   612  						if drop(false) {
   613  							return
   614  						}
   615  						break
   616  					}
   617  				}
   618  			}
   619  		}
   620  	}
   621  }
   622  
   623  func suspproc() {
   624  	s := false
   625  	for {
   626  		select {
   627  		case mouse = <-mousectl.C:
   628  			mousec <- mouse
   629  		case r := <-kbdctl.C:
   630  			switch r {
   631  			case 'q', 'Q', 0x04, 0x7F:
   632  				os.Exit(0)
   633  			default:
   634  				if s {
   635  					s = false
   636  					suspc <- s
   637  					break
   638  				}
   639  				switch r {
   640  				case 'z', 'Z', 'p', 'P', 0x1B:
   641  					s = true
   642  					suspc <- s
   643  				default:
   644  					kbdc <- r
   645  				}
   646  			}
   647  		}
   648  	}
   649  }
   650  
   651  func redraw(new bool) {
   652  	if new {
   653  		if err := display.Attach(draw.RefMesg); err != nil {
   654  			log.Fatalf("can't reattach to window: %v", err)
   655  		}
   656  	}
   657  
   658  	r := screen.R
   659  	pos.X = (pos.X - rboard.Min.X) / pcsz
   660  	pos.Y = (pos.Y - rboard.Min.Y) / pcsz
   661  	dx := r.Max.X - r.Min.X
   662  	dy := r.Max.Y - r.Min.Y - 2*32
   663  	DY = dx / NX
   664  	if DY > dy/NY {
   665  		DY = dy / NY
   666  	}
   667  	DY /= 8
   668  	if DY > 4 {
   669  		DY = 4
   670  	}
   671  	pcsz = DY * 8
   672  	DMOUSE = pcsz / 3
   673  	if pcsz < 8 {
   674  		log.Fatalf("screen too small: %d", pcsz)
   675  	}
   676  	rboard = screenr
   677  	rboard.Min.X += (dx - pcsz*NX) / 2
   678  	rboard.Min.Y += (dy-pcsz*NY)/2 + 32
   679  	rboard.Max.X = rboard.Min.X + NX*pcsz
   680  	rboard.Max.Y = rboard.Min.Y + NY*pcsz
   681  	pscore.X = rboard.Min.X + 8
   682  	pscore.Y = rboard.Min.Y - 32
   683  	scoresz = display.Font.StringSize("000000")
   684  	pos.X = pos.X*pcsz + rboard.Min.X
   685  	pos.Y = pos.Y*pcsz + rboard.Min.Y
   686  	if bb != nil {
   687  		bb.Free()
   688  		bbmask.Free()
   689  		bb2.Free()
   690  		bb2mask.Free()
   691  	}
   692  	bb, _ = display.AllocImage(image.Rect(0, 0, N*pcsz, N*pcsz), screen.Pix, false, 0)
   693  	bbmask, _ = display.AllocImage(image.Rect(0, 0, N*pcsz, N*pcsz), draw.GREY1, false, 0)
   694  	bb2, _ = display.AllocImage(image.Rect(0, 0, N*pcsz, N*pcsz+DY), screen.Pix, false, 0)
   695  	bb2mask, _ = display.AllocImage(image.Rect(0, 0, N*pcsz, N*pcsz+DY), draw.GREY1, false, 0)
   696  	screen.Draw(screenr, display.White, nil, draw.ZP)
   697  	drawboard()
   698  	setpiece(piece)
   699  	if piece != nil {
   700  		drawpiece()
   701  	}
   702  	lastmx = movemouse()
   703  	newscreen = true
   704  	display.Flush()
   705  }
   706  
   707  func Play(pp []Piece, d *draw.Display) {
   708  	pieces = pp
   709  	N = len(pieces[0].d)
   710  	initPieces()
   711  	display = d
   712  	screen = d.ScreenImage
   713  
   714  	mousectl = d.InitMouse()
   715  	kbdctl = d.InitKeyboard()
   716  	rand.Seed(int64(time.Now().UnixNano() % (1e9 - 1)))
   717  	for i, col := range txpix {
   718  		tx[i], _ = d.AllocImage(image.Rect(0, 0, 1, 1), screen.Pix, true, col)
   719  	}
   720  	whitemask, _ = display.AllocImage(image.Rect(0, 0, 1, 1), draw.ARGB32, true, 0x7F7F7F7F)
   721  	tsleep = 50
   722  	timerc = time.Tick(time.Duration(tsleep/2) * time.Millisecond)
   723  	suspc = make(chan bool)
   724  	mousec = make(chan draw.Mouse)
   725  	kbdc = make(chan rune)
   726  	go suspproc()
   727  	redraw(false)
   728  	play()
   729  }