...

Source file src/code.rocket9labs.com/tslocum/bgammon/pkg/server/game.go

Documentation: code.rocket9labs.com/tslocum/bgammon/pkg/server

     1  package server
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"log"
     8  	"time"
     9  
    10  	"code.rocket9labs.com/tslocum/bgammon"
    11  )
    12  
    13  type serverGame struct {
    14  	id         int
    15  	created    int64
    16  	active     int64
    17  	name       []byte
    18  	password   []byte
    19  	client1    *serverClient
    20  	client2    *serverClient
    21  	spectators []*serverClient
    22  	allowed1   []byte
    23  	allowed2   []byte
    24  	account1   int
    25  	account2   int
    26  	inactive   int8
    27  	forefeit   int8
    28  	rematch    int8
    29  	rejoin1    bool
    30  	rejoin2    bool
    31  	replay     [][]byte
    32  	*bgammon.Game
    33  }
    34  
    35  func newServerGame(id int, variant int8) *serverGame {
    36  	now := time.Now().Unix()
    37  	return &serverGame{
    38  		id:      id,
    39  		created: now,
    40  		active:  now,
    41  		Game:    bgammon.NewGame(variant),
    42  	}
    43  }
    44  
    45  func (g *serverGame) playForcedMoves() bool {
    46  	if g.Winner != 0 || len(g.Moves) != 0 || g.client1 == nil || g.client2 == nil {
    47  		return false
    48  	}
    49  	rolls := g.DiceRolls()
    50  	if len(rolls) == 0 {
    51  		return false
    52  	}
    53  	var playerName string
    54  	switch g.Turn {
    55  	case 1:
    56  		if !g.client1.autoplay {
    57  			return false
    58  		}
    59  		playerName = g.Player1.Name
    60  	case 2:
    61  		if !g.client2.autoplay {
    62  			return false
    63  		}
    64  		playerName = g.Player2.Name
    65  	case 0:
    66  		return false
    67  	}
    68  	tb, ok := g.TabulaBoard()
    69  	if !ok {
    70  		return false
    71  	}
    72  	allMoves, allBoards := tb.Available(g.Turn)
    73  	if len(allMoves) == 0 {
    74  		return false
    75  	}
    76  	for i := range allBoards {
    77  		if i == 0 {
    78  			continue
    79  		} else if allBoards[i] != allBoards[0] {
    80  			return false
    81  		}
    82  	}
    83  	var forcedMoves [][2]int8
    84  	for _, m1 := range allMoves[0] {
    85  		if m1[0] == 0 && m1[1] == 0 {
    86  			break
    87  		}
    88  		forcedMoves = append(forcedMoves, m1)
    89  	}
    90  	if len(forcedMoves) == 0 {
    91  		return false
    92  	}
    93  	gc := g.Copy(true)
    94  	for _, move := range forcedMoves {
    95  		if gc.Winner != 0 {
    96  			break
    97  		} else if gc.HaveDiceRoll(move[0], move[1]) == 0 {
    98  			return false
    99  		}
   100  		ok, _ := gc.AddMoves([][]int8{{move[0], move[1]}}, false)
   101  		if !ok {
   102  			log.Printf("ERROR: failed to play forced move during validation %v: %v %v (%v) (%v) (%v)", move, forcedMoves, gc.DiceRolls(), gc, gc.Board, allMoves)
   103  			return false
   104  		}
   105  	}
   106  	g.eachClient(func(client *serverClient) {
   107  		g.sendBoard(client, true)
   108  	})
   109  	for _, move := range forcedMoves {
   110  		if g.HaveDiceRoll(move[0], move[1]) == 0 {
   111  			break
   112  		}
   113  		ok, _ := g.AddMoves([][]int8{{move[0], move[1]}}, false)
   114  		if !ok {
   115  			log.Printf("ERROR: failed to play forced move %v: %v %v (%v) (%v) (%v)", move, forcedMoves, g.DiceRolls(), g.Game, g.Board, allMoves)
   116  			g.eachClient(func(client *serverClient) {
   117  				g.sendBoard(client, false)
   118  			})
   119  			return false
   120  		}
   121  		g.eachClient(func(client *serverClient) {
   122  			ev := &bgammon.EventMoved{
   123  				Moves: bgammon.FlipMoves([][]int8{{move[0], move[1]}}, client.playerNumber, g.Variant),
   124  			}
   125  			ev.Player = playerName
   126  			client.sendEvent(ev)
   127  		})
   128  		if g.handleWin() {
   129  			return true
   130  		}
   131  	}
   132  	g.NextPartialTurn(g.Turn)
   133  	return true
   134  }
   135  
   136  func (g *serverGame) roll(player int8) bool {
   137  	if g.client1 == nil || g.client2 == nil || g.Winner != 0 {
   138  		return false
   139  	}
   140  
   141  	if g.Turn == 0 {
   142  		if player == 1 {
   143  			if g.Roll1 != 0 {
   144  				return false
   145  			}
   146  			g.Roll1 = int8(RandInt(6) + 1)
   147  		} else {
   148  			if g.Roll2 != 0 {
   149  				return false
   150  			}
   151  			g.Roll2 = int8(RandInt(6) + 1)
   152  		}
   153  
   154  		// Only allow the same players to rejoin the game.
   155  		if g.allowed1 == nil {
   156  			g.allowed1, g.allowed2 = g.client1.name, g.client2.name
   157  		}
   158  
   159  		// Store account IDs.
   160  		if g.Started.IsZero() && g.Roll1 != 0 && g.Roll2 != 0 {
   161  			g.Started = time.Now()
   162  			if g.client1.account != nil {
   163  				g.account1 = g.client1.account.id
   164  			}
   165  			if g.client2.account != nil {
   166  				g.account2 = g.client2.account.id
   167  			}
   168  		}
   169  		return true
   170  	} else if player != g.Turn || g.Roll1 != 0 || g.Roll2 != 0 {
   171  		return false
   172  	}
   173  
   174  	g.Roll1 = int8(RandInt(6) + 1)
   175  	g.Roll2 = int8(RandInt(6) + 1)
   176  	if g.Variant == bgammon.VariantTabula {
   177  		g.Roll3 = int8(RandInt(6) + 1)
   178  	}
   179  
   180  	return true
   181  }
   182  
   183  func (g *serverGame) sendBoard(client *serverClient, forcedMove bool) {
   184  	if client.json {
   185  		ev := &bgammon.EventBoard{
   186  			GameState: bgammon.GameState{
   187  				Game:         g.Game,
   188  				PlayerNumber: client.playerNumber,
   189  				Available:    g.LegalMoves(false),
   190  				Forced:       forcedMove,
   191  				Spectating:   g.client1 != client && g.client2 != client,
   192  			},
   193  		}
   194  
   195  		// Reverse spaces for white.
   196  		if client.playerNumber == 2 {
   197  			ev.GameState.Game = ev.GameState.Copy(true)
   198  
   199  			ev.GameState.PlayerNumber = 1
   200  			ev.GameState.Player1, ev.GameState.Player2 = ev.GameState.Player2, ev.GameState.Player1
   201  			ev.GameState.Player1.Number = 1
   202  			ev.GameState.Player2.Number = 2
   203  
   204  			switch ev.GameState.Turn {
   205  			case 1:
   206  				ev.GameState.Turn = 2
   207  			case 2:
   208  				ev.GameState.Turn = 1
   209  			}
   210  
   211  			switch ev.GameState.DoublePlayer {
   212  			case 1:
   213  				ev.GameState.DoublePlayer = 2
   214  			case 2:
   215  				ev.GameState.DoublePlayer = 1
   216  			}
   217  
   218  			switch ev.GameState.Winner {
   219  			case 1:
   220  				ev.GameState.Winner = 2
   221  			case 2:
   222  				ev.GameState.Winner = 1
   223  			}
   224  
   225  			if ev.GameState.Roll1 == 0 || ev.GameState.Roll2 == 0 {
   226  				ev.GameState.Roll1, ev.GameState.Roll2 = ev.GameState.Roll2, ev.GameState.Roll1
   227  			}
   228  
   229  			// Flip board.
   230  			if g.Variant == bgammon.VariantTabula {
   231  				for space := int8(1); space <= 24; space++ {
   232  					ev.Board[space] = g.Game.Board[space] * -1
   233  				}
   234  			} else {
   235  				for space := int8(1); space <= 24; space++ {
   236  					ev.Board[space] = g.Game.Board[bgammon.FlipSpace(space, client.playerNumber, g.Variant)] * -1
   237  				}
   238  			}
   239  			ev.Board[bgammon.SpaceHomePlayer], ev.Board[bgammon.SpaceHomeOpponent] = ev.Board[bgammon.SpaceHomeOpponent]*-1, ev.Board[bgammon.SpaceHomePlayer]*-1
   240  			ev.Board[bgammon.SpaceBarPlayer], ev.Board[bgammon.SpaceBarOpponent] = ev.Board[bgammon.SpaceBarOpponent]*-1, ev.Board[bgammon.SpaceBarPlayer]*-1
   241  			ev.Moves = bgammon.FlipMoves(g.Game.Moves, client.playerNumber, g.Variant)
   242  			ev.GameState.Available = g.LegalMoves(false)
   243  			for i := range ev.GameState.Available {
   244  				ev.GameState.Available[i][0], ev.GameState.Available[i][1] = bgammon.FlipSpace(ev.GameState.Available[i][0], client.playerNumber, g.Variant), bgammon.FlipSpace(ev.GameState.Available[i][1], client.playerNumber, g.Variant)
   245  			}
   246  		}
   247  
   248  		// Sort available moves.
   249  		bgammon.SortMoves(ev.Available)
   250  
   251  		client.sendEvent(ev)
   252  		return
   253  	}
   254  
   255  	scanner := bufio.NewScanner(bytes.NewReader(g.BoardState(client.playerNumber, false)))
   256  	for scanner.Scan() {
   257  		client.sendNotice(string(scanner.Bytes()))
   258  	}
   259  }
   260  
   261  func (g *serverGame) playerCount() int8 {
   262  	var c int8
   263  	if g.client1 != nil {
   264  		c++
   265  	}
   266  	if g.client2 != nil {
   267  		c++
   268  	}
   269  	return c
   270  }
   271  
   272  func (g *serverGame) eachClient(f func(client *serverClient)) {
   273  	if g.client1 != nil {
   274  		f(g.client1)
   275  	}
   276  	if g.client2 != nil {
   277  		f(g.client2)
   278  	}
   279  	for _, spectator := range g.spectators {
   280  		f(spectator)
   281  	}
   282  }
   283  
   284  func (g *serverGame) addClient(client *serverClient) (spectator bool) {
   285  	if g.allowed1 != nil && !bytes.Equal(client.name, g.allowed1) && !bytes.Equal(client.name, g.allowed2) {
   286  		spectator = true
   287  	} else if g.client1 != nil && g.client2 != nil {
   288  		spectator = true
   289  	}
   290  	if spectator {
   291  		for _, spec := range g.spectators {
   292  			if spec == client {
   293  				return true
   294  			}
   295  		}
   296  		client.playerNumber = 1
   297  		g.spectators = append(g.spectators, client)
   298  		ev := &bgammon.EventJoined{
   299  			GameID:       g.id,
   300  			PlayerNumber: 1,
   301  		}
   302  		ev.Player = string(client.name)
   303  		client.sendEvent(ev)
   304  		g.sendBoard(client, false)
   305  		return spectator
   306  	}
   307  
   308  	var playerNumber int8
   309  	defer func() {
   310  		ev := &bgammon.EventJoined{
   311  			GameID:       g.id,
   312  			PlayerNumber: 1,
   313  		}
   314  		ev.Player = string(client.name)
   315  		client.sendEvent(ev)
   316  		g.sendBoard(client, false)
   317  
   318  		if playerNumber == 0 {
   319  			return
   320  		}
   321  
   322  		opponent := g.opponent(client)
   323  		if opponent != nil {
   324  			ev := &bgammon.EventJoined{
   325  				GameID:       g.id,
   326  				PlayerNumber: 2,
   327  			}
   328  			ev.Player = string(client.name)
   329  			opponent.sendEvent(ev)
   330  			g.sendBoard(opponent, false)
   331  		}
   332  
   333  		{
   334  			ev := &bgammon.EventJoined{
   335  				GameID:       g.id,
   336  				PlayerNumber: client.playerNumber,
   337  			}
   338  			ev.Player = string(client.name)
   339  			for _, spectator := range g.spectators {
   340  				spectator.sendEvent(ev)
   341  				g.sendBoard(spectator, false)
   342  			}
   343  		}
   344  
   345  		if playerNumber == 1 {
   346  			g.rejoin1 = true
   347  		} else {
   348  			g.rejoin2 = true
   349  		}
   350  
   351  		if g.forefeit == playerNumber {
   352  			g.forefeit = 0
   353  		}
   354  	}()
   355  	var rating int
   356  	if client.account != nil {
   357  		rating = client.account.casual.getRating(g.Variant, g.Points > 1) / 100
   358  	}
   359  	switch {
   360  	case g.client1 != nil:
   361  		g.client2 = client
   362  		g.Player2.Name = string(client.name)
   363  		g.Player2.Rating = rating
   364  		client.playerNumber = 2
   365  		playerNumber = 2
   366  	case g.client2 != nil:
   367  		g.client1 = client
   368  		g.Player1.Name = string(client.name)
   369  		g.Player1.Rating = rating
   370  		client.playerNumber = 1
   371  		playerNumber = 1
   372  	default:
   373  		if RandInt(2) == 0 {
   374  			g.client1 = client
   375  			g.Player1.Name = string(client.name)
   376  			g.Player1.Rating = rating
   377  			client.playerNumber = 1
   378  			playerNumber = 1
   379  		} else {
   380  			g.client2 = client
   381  			g.Player2.Name = string(client.name)
   382  			g.Player2.Rating = rating
   383  			client.playerNumber = 2
   384  			playerNumber = 2
   385  		}
   386  	}
   387  	return spectator
   388  }
   389  
   390  func (g *serverGame) removeClient(client *serverClient) {
   391  	var playerNumber int
   392  	defer func() {
   393  		if playerNumber == 0 {
   394  			return
   395  		}
   396  
   397  		ev := &bgammon.EventLeft{}
   398  		ev.Player = string(client.name)
   399  
   400  		client.sendEvent(ev)
   401  		if !client.json {
   402  			g.sendBoard(client, false)
   403  		}
   404  
   405  		var opponent *serverClient
   406  		if playerNumber == 1 && g.client2 != nil {
   407  			opponent = g.client2
   408  		} else if playerNumber == 2 && g.client1 != nil {
   409  			opponent = g.client1
   410  		}
   411  		if opponent != nil {
   412  			opponent.sendEvent(ev)
   413  			if !opponent.json {
   414  				g.sendBoard(opponent, false)
   415  			}
   416  		}
   417  
   418  		for _, spectator := range g.spectators {
   419  			spectator.sendEvent(ev)
   420  			if !spectator.json {
   421  				g.sendBoard(spectator, false)
   422  			}
   423  		}
   424  
   425  		if playerNumber == 1 && g.client2 != nil {
   426  			g.forefeit = 1
   427  		} else if playerNumber == 2 && g.client1 != nil {
   428  			g.forefeit = 2
   429  		}
   430  
   431  		client.playerNumber = 0
   432  	}()
   433  	switch {
   434  	case g.client1 == client:
   435  		g.client1 = nil
   436  		g.Player1.Name = ""
   437  		g.Player1.Rating = 0
   438  		playerNumber = 1
   439  	case g.client2 == client:
   440  		g.client2 = nil
   441  		g.Player2.Name = ""
   442  		g.Player2.Rating = 0
   443  		playerNumber = 2
   444  	default:
   445  		for i, spectator := range g.spectators {
   446  			if spectator == client {
   447  				g.spectators = append(g.spectators[:i], g.spectators[i+1:]...)
   448  
   449  				ev := &bgammon.EventLeft{}
   450  				ev.Player = string(client.name)
   451  
   452  				client.sendEvent(ev)
   453  				if !client.json {
   454  					g.sendBoard(client, false)
   455  				}
   456  
   457  				client.playerNumber = 0
   458  				return
   459  			}
   460  		}
   461  		return
   462  	}
   463  }
   464  
   465  func (g *serverGame) opponent(client *serverClient) *serverClient {
   466  	if g.client1 == client {
   467  		return g.client2
   468  	} else if g.client2 == client {
   469  		return g.client1
   470  	}
   471  	return nil
   472  }
   473  
   474  func (g *serverGame) listing(playerName []byte) *bgammon.GameListing {
   475  	if g.terminated() {
   476  		return nil
   477  	}
   478  
   479  	var playerCount int8
   480  	if len(g.allowed1) != 0 && (len(playerName) == 0 || (!bytes.Equal(g.allowed1, playerName) && !bytes.Equal(g.allowed2, playerName))) {
   481  		playerCount = 2
   482  	} else {
   483  		playerCount = g.playerCount()
   484  	}
   485  
   486  	var rating int
   487  	if g.client1 != nil && g.client1.account != nil {
   488  		rating = g.client1.account.casual.getRating(g.Variant, g.Points > 1)
   489  	}
   490  	if g.client2 != nil && g.client2.account != nil {
   491  		r := g.client2.account.casual.getRating(g.Variant, g.Points > 1)
   492  		if r > rating {
   493  			rating = r
   494  		}
   495  	}
   496  
   497  	name := string(g.name)
   498  	switch g.Variant {
   499  	case bgammon.VariantAceyDeucey:
   500  		name = "(Acey-deucey) " + name
   501  	case bgammon.VariantTabula:
   502  		name = "(Tabula) " + name
   503  	}
   504  
   505  	return &bgammon.GameListing{
   506  		ID:       g.id,
   507  		Points:   g.Points,
   508  		Password: len(g.password) != 0,
   509  		Players:  playerCount,
   510  		Rating:   rating / 100,
   511  		Name:     name,
   512  	}
   513  }
   514  
   515  func (g *serverGame) recordEvent() {
   516  	r1, r2, r3 := g.Roll1, g.Roll2, g.Roll3
   517  	if r2 > r1 {
   518  		r1, r2 = r2, r1
   519  	}
   520  	if r3 > r1 {
   521  		r1, r3 = r3, r1
   522  	}
   523  	if r3 > r2 {
   524  		r2, r3 = r3, r2
   525  	}
   526  	var movesFormatted []byte
   527  	if len(g.Moves) != 0 {
   528  		movesFormatted = append([]byte(" "), bgammon.FormatMoves(g.Moves)...)
   529  	}
   530  	line := []byte(fmt.Sprintf("%d r %d-%d", g.Turn, r1, r2))
   531  	if r3 > 0 {
   532  		line = append(line, []byte(fmt.Sprintf("-%d", r3))...)
   533  	}
   534  	line = append(line, movesFormatted...)
   535  	g.replay = append(g.replay, line)
   536  }
   537  
   538  func (g *serverGame) nextTurn(reroll bool) {
   539  	g.Game.NextTurn(reroll)
   540  	if reroll {
   541  		return
   542  	}
   543  
   544  	// Roll automatically.
   545  	if g.Winner == 0 {
   546  		gameState := &bgammon.GameState{
   547  			Game:         g.Game,
   548  			PlayerNumber: g.Turn,
   549  			Available:    g.LegalMoves(false),
   550  		}
   551  		if !gameState.MayDouble() {
   552  			if !g.roll(g.Turn) {
   553  				g.eachClient(func(client *serverClient) {
   554  					client.Terminate("Server error")
   555  				})
   556  				return
   557  			}
   558  			ev := &bgammon.EventRolled{
   559  				Roll1: g.Roll1,
   560  				Roll2: g.Roll2,
   561  				Roll3: g.Roll3,
   562  			}
   563  			if g.Turn == 1 {
   564  				ev.Player = gameState.Player1.Name
   565  			} else {
   566  				ev.Player = gameState.Player2.Name
   567  			}
   568  			g.eachClient(func(client *serverClient) {
   569  				client.sendEvent(ev)
   570  			})
   571  
   572  			// Play forced moves automatically.
   573  			forcedMove := g.playForcedMoves()
   574  			if forcedMove && len(g.LegalMoves(false)) == 0 {
   575  				chooseRoll := g.Variant == bgammon.VariantAceyDeucey && ((g.Roll1 == 1 && g.Roll2 == 2) || (g.Roll1 == 2 && g.Roll2 == 1)) && len(g.Moves) == 2
   576  				if g.Variant != bgammon.VariantAceyDeucey || !chooseRoll {
   577  					g.recordEvent()
   578  					g.nextTurn(false)
   579  					return
   580  				}
   581  			}
   582  		}
   583  	}
   584  
   585  	g.eachClient(func(client *serverClient) {
   586  		g.sendBoard(client, false)
   587  	})
   588  }
   589  
   590  func (g *serverGame) handleWin() bool {
   591  	if g.Winner == 0 {
   592  		return false
   593  	}
   594  	var opponent int8 = 1
   595  	opponentHome := bgammon.SpaceHomePlayer
   596  	opponentEntered := g.Player1.Entered
   597  	playerBar := bgammon.SpaceBarPlayer
   598  	if g.Winner == 1 {
   599  		opponent = 2
   600  		opponentHome = bgammon.SpaceHomeOpponent
   601  		opponentEntered = g.Player2.Entered
   602  		playerBar = bgammon.SpaceBarOpponent
   603  	}
   604  
   605  	backgammon := bgammon.PlayerCheckers(g.Board[playerBar], opponent) != 0
   606  	if !backgammon {
   607  		homeStart, homeEnd := bgammon.HomeRange(g.Winner, g.Variant)
   608  		bgammon.IterateSpaces(homeStart, homeEnd, g.Variant, func(space int8, spaceCount int8) {
   609  			if bgammon.PlayerCheckers(g.Board[space], opponent) != 0 {
   610  				backgammon = true
   611  			}
   612  		})
   613  	}
   614  
   615  	var winPoints int8
   616  	switch g.Variant {
   617  	case bgammon.VariantAceyDeucey:
   618  		for space := int8(0); space < bgammon.BoardSpaces; space++ {
   619  			if (space == bgammon.SpaceHomePlayer || space == bgammon.SpaceHomeOpponent) && opponentEntered {
   620  				continue
   621  			}
   622  			winPoints += bgammon.PlayerCheckers(g.Board[space], opponent)
   623  		}
   624  	case bgammon.VariantTabula:
   625  		winPoints = 1
   626  	default:
   627  		if backgammon {
   628  			winPoints = 3 // Award backgammon.
   629  		} else if g.Board[opponentHome] == 0 {
   630  			winPoints = 2 // Award gammon.
   631  		} else {
   632  			winPoints = 1
   633  		}
   634  	}
   635  
   636  	g.replay = append([][]byte{[]byte(fmt.Sprintf("i %d %s %s %d %d %d %d %d %d", g.Started.Unix(), g.Player1.Name, g.Player2.Name, g.Points, g.Player1.Points, g.Player2.Points, g.Winner, winPoints, g.Variant))}, g.replay...)
   637  
   638  	r1, r2, r3 := g.Roll1, g.Roll2, g.Roll3
   639  	if r2 > r1 {
   640  		r1, r2 = r2, r1
   641  	}
   642  	if r3 > r1 {
   643  		r1, r3 = r3, r1
   644  	}
   645  	if r3 > r2 {
   646  		r2, r3 = r3, r2
   647  	}
   648  	var movesFormatted []byte
   649  	if len(g.Moves) != 0 {
   650  		movesFormatted = append([]byte(" "), bgammon.FormatMoves(g.Moves)...)
   651  	}
   652  	line := []byte(fmt.Sprintf("%d r %d-%d", g.Turn, r1, r2))
   653  	if r3 > 0 {
   654  		line = append(line, []byte(fmt.Sprintf("-%d", r3))...)
   655  	}
   656  	line = append(line, movesFormatted...)
   657  	g.replay = append(g.replay, line)
   658  
   659  	winEvent := &bgammon.EventWin{
   660  		Points: winPoints * g.DoubleValue,
   661  	}
   662  	var reset bool
   663  	if g.Winner == 1 {
   664  		winEvent.Player = g.Player1.Name
   665  		g.Player1.Points = g.Player1.Points + winPoints*g.DoubleValue
   666  		if g.Player1.Points < g.Points {
   667  			reset = true
   668  		} else {
   669  			g.Ended = time.Now()
   670  		}
   671  	} else {
   672  		winEvent.Player = g.Player2.Name
   673  		g.Player2.Points = g.Player2.Points + winPoints*g.DoubleValue
   674  		if g.Player2.Points < g.Points {
   675  			reset = true
   676  		} else {
   677  			g.Ended = time.Now()
   678  		}
   679  	}
   680  
   681  	winType := winPoints
   682  	if g.Variant != bgammon.VariantBackgammon {
   683  		winType = 1
   684  	}
   685  	err := recordGameResult(g, winType, g.replay)
   686  	if err != nil {
   687  		log.Fatalf("failed to record game result: %s", err)
   688  	}
   689  
   690  	if !reset {
   691  		err := recordMatchResult(g, matchTypeCasual)
   692  		if err != nil {
   693  			log.Fatalf("failed to record match result: %s", err)
   694  		}
   695  	} else {
   696  		g.Reset()
   697  		g.replay = g.replay[:0]
   698  	}
   699  	g.eachClient(func(client *serverClient) {
   700  		client.sendEvent(winEvent)
   701  	})
   702  
   703  	if g.client1 != nil && g.client1.account != nil {
   704  		g.Player1.Rating = g.client1.account.casual.getRating(g.Variant, g.Points > 1) / 100
   705  	}
   706  	if g.client2 != nil && g.client2.account != nil {
   707  		g.Player2.Rating = g.client2.account.casual.getRating(g.Variant, g.Points > 1) / 100
   708  	}
   709  	g.eachClient(func(client *serverClient) {
   710  		g.sendBoard(client, false)
   711  	})
   712  	return true
   713  }
   714  
   715  func (g *serverGame) terminated() bool {
   716  	return g.client1 == nil && g.client2 == nil
   717  }
   718  

View as plain text