Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package main
- import (
- "math"
- "strconv"
- "strings"
- )
- type Activity int
- const (
- lives Activity = 0
- dies Activity = 1
- )
- //this is the main part to code
- // distributor divides the work between workers and interacts with other goroutines.
- func distributor(p golParams, d distributorChans, alive chan []cell,worker []chan uint8) {
- world := make([][]byte, p.imageHeight)
- for i := range world {
- world[i] = make([]byte, p.imageWidth)
- }
- // Request the io goroutine to read in the image with the given filename.
- d.io.command <- ioInput
- d.io.filename <- strings.Join([]string{strconv.Itoa(p.imageWidth), strconv.Itoa(p.imageHeight)}, "x")
- // The io goroutine sends the requested image byte by byte, in rows.
- for y := 0; y < p.imageHeight; y++ {
- for x := 0; x < p.imageWidth; x++ {
- val := <-d.io.inputVal
- if val != 0 {
- //fmt.Println("Alive cell at", x, y)
- world[y][x] = val
- }
- }
- }
- //<---------------------------------------------------------------------------------------------------------------------
- // Calculate the new state of Game of Life after the given number of turns.
- // for each turn the worker go routine is executed and the channels start sending the world to
- // and fro
- //fmt.Println("THE NUMBER OF THREADS ARE ",p.threads)
- for turns := 0; turns < p.turns; turns++ {
- // Passing the world in slices for distributor channel to receive and send it to workers.
- // OK so here we making sure we allocating the right height to each worker via their
- // corresponding channel "worker chan[]". To do so we loop through every (h/n)
- // distance in the height. The reason for the -1 and +1 at the start and end of the loop
- // is for the offset.Taking the example with y= -1 to 9 for the first worker .
- // starting at height -1(goes to 15) , the value that we get is going
- // to be fed at position 0(offset for top) in the worker and going up at the end
- // height at (h/n)(8) , the value we get is going to be fed at position 9(offset for bottom)
- // The middle elements are ordered one place to the right so for example 1 becomes 2, 3 becomes 4.
- // The reason for this is because in the worker function , the worker loops from y=0 to y=9
- // when receiving the bytes from distributor .
- // for simplicity think of it like this .value at (index of distributor) "goes to "
- // index of worker . therefore
- // -1(15) goes to 0(top offset) , 0 goes to 1 , 1 goes to 2 , 3 goes to 4 , 4 goes to 5,
- // 5 goes to 6 , 6 goes to 7 , 7 goes to 8 , 8 goes to 9 (bottom offset)
- //---------------------------------------------------------------------------------------------
- // *******STAGE 3 EXPLANATION******************************************
- // ** we are still making sure the right height is allocated the difference here is we take into
- // account that the heights are not evenly distributed since we can get multiples of 2 threads .
- // As a result I came up with a mathematical
- // implementation that divides the heights accordingly between the workers by simply rounding down
- // the height allocated to each worker in which case the next worker starts from the rounded height.
- // Float is used as we don't want to get rid of the decimals during division
- // I applied this to most Iteration through the height of the Image
- for i:=0; i<p.threads; i++ {
- start:=int(math.Floor(float64(p.imageHeight)/float64(p.threads)*float64(i)))
- end:= int(math.Floor((float64(p.imageHeight)/float64(p.threads))*(float64(i)+1)))
- for y:=start-1; y<end+1; y++ {
- for x:=0; x<p.imageWidth; x++ {
- worker[i] <- world[modulo(y,p.imageHeight)][x] // general case including 0 and 15
- }
- }
- }
- // RECONSTRUCTING THE WORLD HERE
- // Here now i'm assembling all the slices together , which is much more simpler
- // for each worker i just wanna loop through its space of h/n . I dont need the
- // offsets as i explained below or above.
- // the worker channel feeds back the updated byte into the world
- for i:=0; i<p.threads; i++ {
- start:=int(math.Floor(float64(p.imageHeight)/float64(p.threads)*float64(i)))
- end:= int(math.Floor((float64(p.imageHeight)/float64(p.threads))*(float64(i)+1)))
- for y := start; y < end; y++ {
- for x := 0; x < p.imageWidth; x++ {
- world[y][x] = <- worker[i]
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------------------------------
- // Create an empty slice to store coordinates of cells that are still alive after p.turns are done.
- var finalAlive []cell
- // Go through the world and append the cells that are still alive.
- for y := 0; y < p.imageHeight; y++ {
- for x := 0; x < p.imageWidth; x++ {
- if world[y][x] != 0x00{
- finalAlive = append(finalAlive, cell{x: x, y: y})
- }
- }
- }
- // output the pgm files
- outputWorld(p,world,d)
- // Make sure that the Io has finished any output before exiting.
- d.io.command <- ioCheckIdle
- <-d.io.idle
- // Return the coordinates of cells that are still alive.
- alive <- finalAlive //FOR TESTING FRAMEWORK
- }
- // END OF DISTRIBUTOR FUNCTION <-----------------------------------------------------------------------------------------------------------------
- // SOME GENERAL FUNCTIONS AND OTHER GO ROUTINE FUNCTIONS
- // function to output the world in a pgm file using the channel
- func outputWorld(p golParams, world [][] byte, d distributorChans) {
- d.io.command <- ioOutput
- d.io.filename <- strings.Join([]string{strconv.Itoa(p.imageWidth), strconv.Itoa(p.imageHeight), strconv.Itoa(p.turns)}, "x")
- for y := 0; y < p.imageHeight; y++ {
- for x := 0; x < p.imageWidth; x++ {
- d.io.outputVal <- world[y][x]
- }
- }
- }
- //-----------------------------------------------------------------------------------------------------------------------------------------------
- // go routine
- // worker function that deals with the part of the world allocated and performs game of life logic
- func worker(p golParams , sliceWorld chan uint8, startY int , endY int ) {
- //********************** STAGE 3 EXPLANATION*************************************************************
- // startY and endY are the corresponding index of the world within which each worker is allocated (check main.go)
- // difference between these indexes gives the height that is allocated to each workers
- // so here the p.imageheight/p.thread is simply refracted to endY-startY
- workerWorld := make([][]byte,(endY-startY) + 2 )
- for i:=range workerWorld {
- workerWorld[i] = make([]byte, p.imageWidth)
- }
- // sending the slice of world to workerWorld through the distributor channel
- // We need this infinite for loop so that the worker channels are executed as long as it has to
- // until main function ends the go routines
- for {
- for y:= 0; y < (endY-startY)+2; y++ {
- for x := 0; x < p.imageWidth; x++ {
- workerWorld[y][x] = <-sliceWorld
- }
- }
- preSlice := make([][]byte,(endY-startY)+2)
- for i := range preSlice {
- preSlice[i] = make([]byte, p.imageWidth)
- }
- for i := range preSlice {
- copy(preSlice[i], workerWorld[i])
- }
- for y := 1; y < (endY-startY)+1; y++ {
- for x := 0; x < p.imageWidth; x++ {
- // Placeholder for the actual Game of Life logic: flips alive cells to dead and dead cells to alive
- if CellActivity(p, x, y, preSlice) == lives {
- workerWorld[y][x] = 0xFF
- } // cell lives
- if CellActivity(p, x, y, preSlice) == dies {
- workerWorld[y][x] = 0x00
- } // cell dies
- }
- }
- // Sends back the slice to distributor
- for y := 1; y < (endY-startY)+1; y++ {
- for x := 0; x < p.imageWidth; x++ {
- sliceWorld <- workerWorld[y][x]
- }
- }
- }
- //-------------------------------------------------------------------------------------------------------
- }
- //function that generates cell activity . Modified to work with workers and their offsets
- func CellActivity(p golParams ,x int ,y int, world [][] byte ) Activity {
- aliveNeighbour := 0
- for i := -1; i < 2; i++ {
- for j := -1; j < 2; j++ {
- if !(i == 0 && j == 0 ) {
- // we do not need to do the modulo of the height cause we
- // have the offsets hence since we start at y=1 to 8
- // checking cells at -1 and +1 at the top and bottom edges(wrapping around)
- // is simply just checking cells at y=0 and y=9
- if world[(y+i)][modulo(x+j, p.imageWidth)] == 0xFF {
- aliveNeighbour++
- }
- }
- }
- }
- if aliveNeighbour < 2 || aliveNeighbour >3 {return dies}
- if aliveNeighbour == 2 && world[y][x]==0 {return dies}
- return lives
- }
- // Return the arithmetic modulo of a number
- func modulo(x int , mod int ) int {
- if x>=mod { x= x%mod }
- if x<0 { x= x + mod }
- return x
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement