public void setCASettings(ICASettings caSettings) { this.caSettings = caSettings; Point[] neighb = caSettings.Neighborhood; invNeighborhood = new Point[neighb.Length]; for(int i = 0; i < invNeighborhood.Length; i++) { Point p = new Point(neighb[i]); p.x *= -1; p.y *= -1; invNeighborhood[i] = p; } }
/** * Set the range of points this thread should process * * This causes processing to begin. * * @param pointArr The array of points that need checking * @param start The index to start processing at inclusive * @param end The index to stop processing at exclusive **/ public void setRange(Point[] pointArr, int start, int end) { lock(rangeLock) { this.pointArr = pointArr; this.start = start; this.end = end; rangeSet = true; Monitor.PulseAll(rangeLock); } }
/** * Calculate the next state of the point at (x,y) * * This involves constructing the neighborhood taking into account wraparound * Then calling the nextState function * * @param x The x coordinate of the point * @param y The y coordinate of the point * * @return The next state of that point **/ private uint nextState(int x, int y) { Point[] neighborhood = caSettings.Neighborhood; uint[] nVals = new uint[neighborhood.Length]; for(int i = 0; i < neighborhood.Length; i++) { Point p = new Point(neighborhood[i]); p.x = sanitized(p.x + x); p.y = sanitized(p.y + y); nVals[i] = board[p.x][p.y]; } return caSettings.nextState(nVals); }
/** * Step the simulation one step. Return any state changes. * * This involves calculating which points to check and running nextState on each point. * If the state differs from the current state, mark it down. **/ public IDictionary<Point, uint> step() { IDictionary<Point, uint> changes = new Dictionary<Point, uint>(); // Initially the entire board needs to be checked. if(changed == null) { for(int i = 0; i < board.Length; i++) { for(int j = 0; j < board[i].Length; j++) { uint val = nextState(i,j); if(val != board[i][j]) { changes[new Point(i,j)] = val; } } } // Otherwise check only the points that could have changed } else { // Calculate the points that need to be checked HashSet<Point> points = nextRound(changed); // If there are less than 50, just run serially. if(points.Count < 50) { foreach(Point p in points) { uint val = nextState(p.x,p.y); if(val != board[p.x][p.y]) { changes[p] = val; } } // Otherwise use the step threads } else { // First copy the points into an array. This allows for easy partitioning of the work Point[] pts = new Point[points.Count]; points.CopyTo(pts); // Because I'm not sure if I need this or not but I might so here it is. Thread.MemoryBarrier(); // Calculate about how many points each thread should check int step = pts.Length / stepThreads.Length; int start = 0; // Set the range of each thread. // Once the range is set, that thread will start processing its points asynchronously // The final step thread needs to check to the end of the array instead of just <step> places for(int i = 0; i < stepThreads.Length; i++) { stepThreads[i].setRange(pts, start, ((i + 1) < stepThreads.Length) ? start + step : pts.Length); start += step; } // Join the values each thread comes up with back into one changes map. // The results method blocks until the calculations for that thread are available. foreach(StepPart p in stepThreads) { foreach(KeyValuePair<Point, uint> kv in p.results()) { changes[kv.Key] = kv.Value; } } } } // Update the set of points that changed and update the board state. changed = changes.Keys; foreach (Point p in changed) { board[p.x][p.y] = changes[p]; } return changes; }
public IDictionary<Point, uint> step() { IDictionary<Point, uint> changes = new Dictionary<Point, uint>(); if(changed == null) { for(int i = 0; i < board.Length; i++) { for(int j = 0; j < board[i].Length; j++) { uint val = nextState(i,j); if(val != board[i][j]) { changes[new Point(i,j)] = val; } } } } else { HashSet<Point> points = nextRound(changed); if(points.Count < 50) { foreach(Point p in points) { uint val = nextState(p.x,p.y); if(val != board[p.x][p.y]) { changes[p] = val; } } } else { Point[] pts = new Point[points.Count]; points.CopyTo(pts); int step = pts.Length / stepThreads.Length; int start = 0; for(int i = 0; i < stepThreads.Length; i++) { stepThreads[i].setRange(pts, start, ((i + 1) < stepThreads.Length) ? start + step : pts.Length); start += step; } foreach(StepPart p in stepThreads) { foreach(KeyValuePair<Point, uint> kv in p.results()) { changes[kv.Key] = kv.Value; } } } } changed = changes.Keys; foreach (Point p in changed) { board[p.x][p.y] = changes[p]; } return changes; }
public void toggleState(Point p) { enqueue(() => { if(curState == State.Stopped) { uint newState = (board[p.x][p.y] + 1) % numStates; pendingChanges[p] = newState; var dict = new Dictionary<Point, uint>(); dict[p] = newState; updateUI(dict); } else { sendError(CAErrorType.Update, "Simulation must be stopped before points can be updated"); } }); }
public void setState(Point p, uint state) { enqueue(() => { if(curState == State.Stopped) { pendingChanges[p] = state; var dict = new Dictionary<Point, uint>(); dict[p] = state; updateUI(dict); } else { sendError(CAErrorType.Update, "Simulation must be stopped before points can be updated"); } }); }