scala - oop -- mutual dependency between chessboard and its pieces -


there have been many similar questions on mutual dependencies, each leaves me unsure own design.

i writing chess program learn scala. close relationship between board , pieces makes me wonder if piece object should contain reference board belongs to. approach when wrote chess program in java.

however, means board not defined until has pieces, , vice-versa. codependency no problem if board instance variable can add pieces build board, goes against immutability.

related:

the approach here seem suggest defining rules of piece movement in board object: https://gamedev.stackexchange.com/questions/43681/how-to-avoid-circular-dependencies-between-player-and-world

the highest voted answer here has similar suggestion: two objects dependencies each other. bad?

the selected answer above link different -- moves mutual dependency class definitions interfaces. don't understand why better.

the design here mirrors current approach: https://sourcemaking.com/refactoring/change-bidirectional-association-to-unidirectional

my code:

abstract class piece(val side: side.value, val row: int, val col: int){   val piece_type: piecetype.value //isinstanceof() accomplish same   def possible_moves(board: board): list[move] }  class board (val pieces: array[array[piece]]){   def this(){     this(defaultboard.setup) //an object builds starting board   }  } 

having pass parameter board piece belongs work, feels wrong.

thanks in advance!

i took liberty redesign classes.

the first thing noticed: piece isn't piece. there white bishop on top left field. if move top right field, become different piece? - not. therefore, position of piece on board not part of identity.

so refactor class piece this:

trait piece {   def side:side.value   def piece_type:piecetype.value } 

(i used trait instead of abstract class here leave open implementor how implement 2 methods.)

the information lost in process should put in different type:

case class pieceplacement(piece:piece, row:int, col:int) {   def possible_moves(board:board):seq[move] = ??? // why enfore list here? } 

now can define board this:

case class board(pieces:indexedseq[indexedseq[piece]] = defaultboard.setup) 

(notice how replaced auxiliary constructor default parameter value, , used immutable indexedseq instead of mutable array.)

if have dependency between placement of piece , board, can this:

  1. add board pieceplacement:

case class pieceplacement(piece:piece, row:int, col:int, board:board) {...}

  1. make board create placement instances:

case class board(...) { def place(piece:piece, row:int, col:int):(board,pieceplacement) = ??? }

note return value of place returns not new pieceplacement instance, new board instance, because want work immutable instances.

now, if @ this, question should raised why place returns pieceplacement. benefit caller ever have of it? it's pretty board-internal information. therefore, refactor place method such returns new board. then, consequently go without placement type altogether , thereby eliminate mutual dependency.

another thing might want notice place method cannot implemented. returned board must new instance, returned pieceplacement must contain new board instance. since new board instance contains pieceplacement instance, can never created in immutable way.

so follow advice of @jörgwmittag , rid of mutual references. start defining traits boards , pieces, , include absolute minimum of necessary information. example:

trait board {   def at(row:int, col:int):option[piece]   def withpieceat(piece:piece, row:int, col:int):board   def withoutpieceat(row:int, col:int):board }  sealed trait move case class movement(startrow:int, startcol:int, endrow:int, endcol:int) extends move case class capture(startrow:int, startcol:int, endrow:int, endcol:int) extends move  sealed trait piecetype {   def possiblemoves(board:board, row:int, col:int):seq[move] } object pawn extends piecetype {...} object bishop extends piecetype {...}  sealed trait piece {   def side:side.value   def piecetype:piecetype } case class whitepiece(piecetype:piecetype) {   def side:side.white } case class blackpiece(piecetype:piecetype) {   def side:side.black } 

now can start writing code uses traits reason potential moves etc. also, can write classes implement traits. can start off straightforward implementation, , optimize necessary.

for example: every board position has 13 possible states. 1 per chess piece type, times 2 two sides, plus 1 empty state. states enumerable, might optimize enumerating them.

another potential optimization: since board position requires 4 bits modelling it, 1 whole row of board fits int variable when encoded. therefore, whole board state can represented 8 ints, or 4 longs. optimization trade off performance (bit shifting) in favor of memory usage. optimization better algorithm generates huge number of board instances , gets in danger of getting outofmemoryerror.

by modelling board , pieces traits rather classes, can exchange implementations, play around them, , see implementation works best use case - without having change bit of algorithms make use of boards , pieces.

bottom line: introduce things methods , variables when needed. every line of code don't write line of code cannot contain bug. don't worry mutual dependencies between objects when not strictly necessary. , in case of chess board model, not necessary.

focus on simplicity first. every single method, class , parameter should justify existence.

for example, in model proposed, there 2 parameters position: row , col. since there 64 possible positions, argue make position type. can anyval type compiled int, example. then, wouldn't need nested structure store board. can store 64 board placement information objects, that's it.

introduce minimum necessary, , extend when needed. in extreme case, start empty traits , add methods when cannot go on without them. while you're @ it, write unit tests every single method. way, should good, clean , reusable solution. key of reusability is: avoid features as can. every single feature introduce restricts versatility. put in strictly required.


Comments

Popular posts from this blog

html - Firefox flex bug applied to buttons? -

html - Missing border-right in select on Firefox -

python - build a suggestions list using fuzzywuzzy -