Advertisement
Guest User

stage3

a guest
Nov 21st, 2019
149
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 8.86 KB | None | 0 0
  1. package main
  2.  
  3. import (
  4.     "math"
  5.     "strconv"
  6.     "strings"
  7. )
  8.  
  9. type Activity int
  10.  
  11. const (
  12.     lives Activity = 0
  13.     dies  Activity = 1
  14. )
  15.  
  16. //this is the main part to code
  17. // distributor divides the work between workers and interacts with other goroutines.
  18. func distributor(p golParams, d distributorChans, alive chan []cell,worker []chan uint8) {
  19.  
  20.  
  21.     world := make([][]byte, p.imageHeight)
  22.     for i := range world {
  23.         world[i] = make([]byte, p.imageWidth)
  24.     }
  25.  
  26.     // Request the io goroutine to read in the image with the given filename.
  27.     d.io.command <- ioInput
  28.     d.io.filename <- strings.Join([]string{strconv.Itoa(p.imageWidth), strconv.Itoa(p.imageHeight)}, "x")
  29.  
  30.     // The io goroutine sends the requested image byte by byte, in rows.
  31.     for y := 0; y < p.imageHeight; y++ {
  32.         for x := 0; x < p.imageWidth; x++ {
  33.             val := <-d.io.inputVal
  34.             if val != 0 {
  35.                 //fmt.Println("Alive cell at", x, y)
  36.                 world[y][x] = val
  37.             }
  38.         }
  39.     }
  40.  
  41.  
  42.  
  43.  //<---------------------------------------------------------------------------------------------------------------------
  44.  
  45.  // Calculate the new state of Game of Life after the given number of turns.
  46.     // for each turn the worker go routine is executed and the channels start sending the world to
  47.     // and fro
  48.     //fmt.Println("THE NUMBER OF THREADS ARE ",p.threads)
  49.     for turns := 0; turns < p.turns; turns++ {
  50.         // Passing the world in slices for distributor channel to receive and send it to workers.
  51.         // OK so here we making sure we allocating the right height to each worker via their
  52.           // corresponding channel "worker chan[]". To do so we loop through every (h/n)
  53.           // distance in the height. The reason for the -1 and +1 at the start and end of the loop
  54.           // is for the offset.Taking the example with y= -1 to 9 for the first worker .
  55.           // starting at height -1(goes to 15) , the value that we get is going
  56.           // to be fed at position 0(offset for top)  in the worker and going up at the end
  57.           // height at (h/n)(8) , the value we get is going to be fed at position 9(offset for bottom)
  58.           // The middle elements are ordered one place to the right so for example 1 becomes 2, 3 becomes 4.
  59.           // The reason for this is because in the worker function , the worker loops from y=0 to  y=9
  60.           // when receiving the bytes from distributor .
  61.           // for simplicity think of it like this .value at (index of distributor) "goes to "
  62.           // index of worker . therefore
  63.           // -1(15) goes to 0(top offset) , 0 goes to 1 , 1 goes to 2 , 3 goes to 4 , 4 goes to 5,
  64.           // 5 goes to 6 , 6 goes to 7 , 7 goes to 8 , 8 goes to 9 (bottom offset)
  65. //---------------------------------------------------------------------------------------------
  66.  
  67.  
  68. //                                *******STAGE 3 EXPLANATION******************************************
  69.        // ** we are still making sure the right height is allocated the difference here is we take into
  70.        // account that the heights are not evenly distributed since we can get multiples of 2 threads .
  71.        // As a result I came up with a mathematical
  72.        // implementation that divides the heights accordingly between the workers by simply rounding down
  73.        // the height allocated to each worker in which case the next worker starts from the rounded height.
  74.        // Float is used as we don't want to get rid of the decimals during division
  75.        // I applied this to most  Iteration through the height of the Image
  76.         for i:=0; i<p.threads; i++ {
  77.             start:=int(math.Floor(float64(p.imageHeight)/float64(p.threads)*float64(i)))
  78.             end:= int(math.Floor((float64(p.imageHeight)/float64(p.threads))*(float64(i)+1)))
  79.             for y:=start-1; y<end+1; y++ {
  80.                 for x:=0; x<p.imageWidth; x++ {
  81.                     worker[i] <- world[modulo(y,p.imageHeight)][x] // general case including 0 and 15
  82.                 }
  83.             }
  84.         }
  85.  
  86.         // RECONSTRUCTING THE WORLD HERE
  87.         // Here now i'm assembling all the slices together , which is much more simpler
  88.         // for each worker i just wanna loop through its   space of h/n . I dont need the
  89.         // offsets as i explained below or above.
  90.         // the worker channel feeds back the updated byte into the world
  91.  
  92.         for i:=0; i<p.threads; i++ {
  93.             start:=int(math.Floor(float64(p.imageHeight)/float64(p.threads)*float64(i)))
  94.             end:= int(math.Floor((float64(p.imageHeight)/float64(p.threads))*(float64(i)+1)))
  95.             for y := start; y < end; y++ {
  96.                 for x := 0; x < p.imageWidth; x++ {
  97.                  world[y][x] = <- worker[i]
  98.                 }
  99.             }
  100.         }
  101.     }
  102.  
  103. //-------------------------------------------------------------------------------------------------------------------------
  104.  
  105.     // Create an empty slice to store coordinates of cells that are still alive after p.turns are done.
  106.     var finalAlive []cell
  107.     // Go through the world and append the cells that are still alive.
  108.     for y := 0; y < p.imageHeight; y++ {
  109.         for x := 0; x < p.imageWidth; x++ {
  110.             if world[y][x] != 0x00{
  111.                 finalAlive = append(finalAlive, cell{x: x, y: y})
  112.             }
  113.         }
  114.     }
  115.     // output the pgm files
  116.     outputWorld(p,world,d)
  117.  
  118.     // Make sure that the Io has finished any output before exiting.
  119.     d.io.command <- ioCheckIdle
  120.     <-d.io.idle
  121.  
  122.     // Return the coordinates of cells that are still alive.
  123.     alive <- finalAlive //FOR TESTING FRAMEWORK
  124.  
  125. }
  126.  
  127. // END OF DISTRIBUTOR FUNCTION <-----------------------------------------------------------------------------------------------------------------
  128.  
  129. // SOME GENERAL FUNCTIONS AND OTHER GO ROUTINE FUNCTIONS
  130.  
  131.  
  132.  
  133. // function to output the world in a pgm file using the channel
  134. func outputWorld(p golParams, world [][] byte, d distributorChans) {
  135.       d.io.command <- ioOutput
  136.       d.io.filename <- strings.Join([]string{strconv.Itoa(p.imageWidth), strconv.Itoa(p.imageHeight), strconv.Itoa(p.turns)}, "x")
  137.       for y := 0; y < p.imageHeight; y++ {
  138.           for x := 0; x < p.imageWidth; x++ {
  139.               d.io.outputVal <- world[y][x]
  140.  
  141.           }
  142.       }
  143.   }
  144. //-----------------------------------------------------------------------------------------------------------------------------------------------
  145.  
  146. // go routine
  147. // worker function that deals with the part of the world allocated and performs game of life logic
  148. func worker(p golParams , sliceWorld chan  uint8, startY int , endY int ) {
  149.  
  150.     //********************** STAGE 3 EXPLANATION*************************************************************
  151.  
  152.     // startY and endY are the corresponding  index of the world  within which each worker is allocated (check main.go)
  153.     // difference between these indexes gives the height that is allocated to  each  workers
  154.     // so here the p.imageheight/p.thread is simply refracted to  endY-startY
  155.  
  156.     workerWorld := make([][]byte,(endY-startY) + 2  )
  157.  
  158.     for i:=range workerWorld {
  159.         workerWorld[i] = make([]byte, p.imageWidth)
  160.     }
  161.     // sending the slice of world to workerWorld through the distributor channel
  162.     // We need this infinite for loop so that the worker channels are executed as long as it has to
  163.     // until main function ends the go routines
  164.     for {
  165.         for y:= 0; y < (endY-startY)+2; y++ {
  166.             for x := 0; x < p.imageWidth; x++ {
  167.                 workerWorld[y][x] = <-sliceWorld
  168.             }
  169.         }
  170.  
  171.         preSlice := make([][]byte,(endY-startY)+2)
  172.         for i := range preSlice {
  173.             preSlice[i] = make([]byte, p.imageWidth)
  174.         }
  175.         for i := range preSlice {
  176.             copy(preSlice[i], workerWorld[i])
  177.         }
  178.  
  179.         for y := 1; y < (endY-startY)+1; y++ {
  180.             for x := 0; x < p.imageWidth; x++ {
  181.                 // Placeholder for the actual Game of Life logic: flips alive cells to dead and dead cells to alive
  182.                 if CellActivity(p, x, y, preSlice) == lives {
  183.                     workerWorld[y][x] = 0xFF
  184.                 } // cell lives
  185.                 if CellActivity(p, x, y, preSlice) == dies {
  186.                     workerWorld[y][x] = 0x00
  187.                 } //  cell dies
  188.             }
  189.         }
  190.  
  191.  
  192.         // Sends back the slice to distributor
  193.         for y := 1; y < (endY-startY)+1; y++ {
  194.             for x := 0; x < p.imageWidth; x++ {
  195.                 sliceWorld <- workerWorld[y][x]
  196.             }
  197.         }
  198.     }
  199.  
  200.  
  201. //-------------------------------------------------------------------------------------------------------
  202.  
  203.  
  204. }
  205.  
  206.  
  207. //function that generates cell activity . Modified to work with workers and their offsets
  208. func CellActivity(p golParams ,x int ,y int, world [][] byte ) Activity {
  209.     aliveNeighbour := 0
  210.     for i := -1; i < 2; i++ {
  211.         for j := -1; j < 2; j++ {
  212.             if !(i == 0 && j == 0 ) {
  213.                 // we do not need to do the modulo of the height cause we
  214.                 // have the offsets hence  since we start at y=1 to 8
  215.                 // checking cells at -1 and +1 at the top and bottom edges(wrapping around)
  216.                 // is simply just checking cells at y=0 and y=9
  217.                 if world[(y+i)][modulo(x+j, p.imageWidth)] == 0xFF {
  218.                     aliveNeighbour++
  219.                 }
  220.             }
  221.         }
  222.     }
  223.     if aliveNeighbour < 2 || aliveNeighbour >3  {return dies}
  224.     if aliveNeighbour == 2 && world[y][x]==0 {return dies}
  225.     return lives
  226. }
  227.  
  228. // Return the arithmetic modulo of a number
  229. func modulo(x int , mod int ) int {
  230.     if x>=mod { x= x%mod }
  231.     if x<0 { x= x + mod }
  232.     return x
  233. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement