Advertisement
Guest User

/r/dailyprogrammer Intermediate 206 by h2g2_researcher

a guest
Mar 23rd, 2015
219
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 10.26 KB | None | 0 0
  1. #include <iostream>
  2. #include <array>
  3. #include <cassert>
  4. #include <future>
  5. #include <string>
  6. #ifndef NDEBUG
  7. #include <fstream>
  8. #include <sstream>
  9. #endif
  10.  
  11. using namespace std;
  12.  
  13. template <typename T>
  14. bool isPowOf2(T val)
  15. {
  16.     return val == 1 || (val % 2 == 0 && isPowOf2(val / 2));
  17. };
  18.  
  19. #ifndef NDEBUG
  20. template <class T>
  21. struct FakeFuture{
  22.     T val; T get() const { return val; }; FakeFuture operator=(const T& v) { val = v; return *this; }
  23. };
  24. #endif
  25.  
  26. /*
  27. Creates an array of 4 tiles. Each sub-tile is SIZE/2.
  28. Tiles are swizzled:
  29. +-+-+
  30. |0|2|
  31. +-+-+
  32. |1|3|
  33. +-+-+
  34. x- left to right, y- is top to bottom.
  35. This keeps data physically close near each other in the cache, for much improved
  36. lookup times when accessing lots of data in the same region.
  37. */
  38. template <typename T>
  39. class TiledArray
  40. {
  41.     array<T*, 4> data;
  42.     array<T*, 4> buildData(size_t size, T* ptr) const
  43.     {
  44.         const auto stride = size*size/4;
  45.         array<T*, 4> result;
  46.         for (auto i = 0u; i < result.size(); ++i)
  47.         {
  48.             result[i] = ptr + stride*i;
  49.         }
  50.         return result;
  51.     }
  52.     const size_t SIZE;
  53.     char& getValue(size_t x, size_t y) const
  54.     {
  55.         assert(x < SIZE);
  56.         assert(y < SIZE);
  57.         auto nextSize = SIZE / 2;
  58.         const size_t index = (x < nextSize ? 0 : 2) + (y < nextSize ? 0 : 1);
  59.         return (SIZE > 2 ? TiledArray(nextSize, data[index]).at(x%nextSize, y%nextSize) : *data[index]);
  60.     }
  61. public:
  62.     // sideSize must be a power of 2.
  63.     // ptr must point to an array with at elast sideSize^2 elements.
  64.     TiledArray(size_t sideSize, T* ptr) : data(buildData(sideSize, ptr)), SIZE(sideSize)
  65.     {
  66.         assert(isPowOf2(sideSize));
  67.     }
  68.     TiledArray(const TiledArray&) = default;
  69.     TiledArray(TiledArray&& r) : data(move(r.data)), SIZE(r.SIZE) {}
  70.    
  71.     size_t size() const { return SIZE; }
  72.  
  73.     static size_t num_elements(size_t size) { assert(isPowOf2(size)); return size*size; }
  74.     static size_t num_subtiles(size_t size) { assert(isPowOf2(size)); return size > 1 ? 1 + 4*num_subtiles(size / 2) : 0; }
  75.  
  76.     size_t num_elements() const { return num_elements(SIZE); }
  77.     size_t num_subtiles() const { return num_subtiles(SIZE); }
  78.    
  79.     const T& at(size_t x, size_t y) const
  80.     {
  81.         return getValue(x, y);
  82.     }
  83.  
  84.     T& at(size_t x, size_t y)
  85.     {
  86.         return getValue(x, y);
  87.     }
  88.  
  89.     TiledArray<T> getSubtile(unsigned int index) const
  90.     {
  91.         assert(index < 4 && SIZE > 1);
  92.         return TiledArray<T>(SIZE / 2, data[index]);
  93.     }
  94.  
  95.     T* ptr()
  96.     {
  97.         return data[0];
  98.     }
  99.  
  100.     const T* ptr() const
  101.     {
  102.         return data[0];
  103.     }
  104.  
  105.     TiledArray<T> operator=(const TiledArray<T>& r) const
  106.     {
  107.         this->data = r.data;
  108.         this->SIZE = r.SIZE;
  109.         return *this;
  110.     }
  111.  
  112.     TiledArray<T> operator=(TiledArray<T>&& r) const
  113.     {
  114.         this->data = move(r.data);
  115.         this->SIZE = move(r.SIZE);
  116.         return *this;
  117.     }
  118. };
  119.  
  120. // results must point to an array with at least 1 element per subtile in arr.
  121. // Counts the number of times val appears, and places this number in results.
  122. // results is also filled for each subarray.
  123. // This will create a lookup table of the number of plants in each tile.
  124. int countElements(const TiledArray<char> arr, int* results, char val, const int* start, const size_t size)
  125. {
  126.     assert(results - start < static_cast<int>(size));
  127.     switch (arr.size())
  128.     {
  129.     case 0:
  130.     case 1:
  131.         assert(false);
  132.         break;
  133.     case 2:
  134.         *results = count(arr.ptr(), arr.ptr() + arr.num_elements(), val);
  135.         break;
  136.     default:
  137.     {
  138.         auto subSize = arr.getSubtile(0).num_subtiles();
  139. #ifdef NDEBUG
  140.         array<future<int>, 4> subResults;
  141. #else
  142.         array<FakeFuture<int>, 4> subResults;
  143. #endif
  144.         for (auto i(0u); i < subResults.size(); ++i)
  145.         {
  146.             auto resultBuffer = results + 1 + i*subSize;
  147. #ifdef NDEBUG
  148.             subResults[i] = async(countElements, arr.getSubtile(i), resultBuffer, val, start, size);
  149. #else
  150.             subResults[i] = countElements(arr.getSubtile(i), resultBuffer, val, start, size);
  151. #endif
  152.         }
  153.         *results = 0;
  154.         for (auto& res : subResults)
  155.         {
  156.             *results += res.get();
  157.         }
  158.     }
  159.     }
  160.     return *results;
  161. }
  162.  
  163. struct Circle
  164. {
  165.     int x, y, r;
  166. };
  167.  
  168. bool isPointWithinCircle(int x, int y, const Circle& circle)
  169. {
  170.     x -= circle.x;
  171.     y -= circle.y;
  172.     return (x*x + y*y) < (circle.r*circle.r);
  173. };
  174.  
  175. // Count the number of 'val' appearences on the specified tile within the specified circle.
  176. // The size cache given *must* be generated using 'val', or else the results are undefined.
  177. int evaluateCircle(const TiledArray<char> data, const int* sizeCache, const Circle& circle, char val)
  178. {
  179.     assert(data.size() > 0);
  180.  
  181.     // Check for whether the circle is close enough to this tile to affect it.
  182.     if (circle.x < 0 - circle.r || circle.x > static_cast<int>(data.size()) + circle.r
  183.         || circle.y < 0 - circle.r || circle.y > static_cast<int>(data.size()) + circle.r)
  184.     {
  185.         return 0;
  186.     }
  187.  
  188.     // Check all whether all four corners of the tile are inside the circle.
  189.     const bool topLeft = isPointWithinCircle(0, 0,circle);
  190.     const bool topRight = isPointWithinCircle(data.size() - 1, 0,circle);
  191.     const bool bottomLeft = isPointWithinCircle(0, data.size() - 1,circle);
  192.     const bool bottomRight = isPointWithinCircle(data.size() - 1, data.size() - 1,circle);
  193.  
  194.     if (data.size() == 1u)
  195.     {
  196.         // Sizecache doesn't go to this resolution, but there's only one square anyway, so just read from it.
  197.         return (data.at(0, 0) == val && (topLeft||topRight||bottomLeft||bottomRight) && circle.x != 0 && circle.y != 0 ? 1 : 0);
  198.     }
  199.    
  200.     int result = 0;
  201.     if (topLeft && topRight && bottomLeft && bottomRight)
  202.     {
  203.         // Whole tile is covered.
  204.         result = *sizeCache;
  205.  
  206.         // Check whether we've stomped on a plant.
  207.         if (circle.x < static_cast<int>(data.size()) && circle.x >= 0 && circle.y < static_cast<int>(data.size()) && circle.y >= 0 && data.at(circle.x,circle.y) == val)
  208.             --result;
  209.     }
  210.     else
  211.     {
  212.         int sizeCacheStride = data.getSubtile(0).num_subtiles();
  213.         for (int i(0); i < 4; ++i)
  214.         {
  215.             int x = i < 2 ? 0 : data.size() / 2;
  216.             int y = i % 2 == 0 ? 0 : data.size() / 2;
  217.             result += evaluateCircle(data.getSubtile(i), sizeCache + 1 + i*sizeCacheStride, { circle.x - x, circle.y - y, circle.r }, val);
  218.         }
  219.     }
  220.     return result;
  221. }
  222.  
  223. struct Result
  224. {
  225.     int x, y, value;
  226. };
  227.  
  228.  
  229. // Each tile gets searched in its own thread.
  230. vector<Result> searchForMaximum(const TiledArray<char>& masterData, const int* masterSizeCache,
  231.     const TiledArray<char>& thisData, const int* thisSizeCache, int topLeftX, int topLeftY, int radius, char val)
  232. {
  233.     if (thisData.size() == 1)
  234.     {
  235.         return vector<Result>{{ topLeftX, topLeftY, evaluateCircle(masterData, masterSizeCache, { topLeftX, topLeftY, radius }, val) }};
  236.     }
  237.     else
  238.     {
  239. #ifdef NDEBUG
  240.         array<future<vector<Result>>, 4> subResults;
  241. #else
  242.         array<FakeFuture<vector<Result>>, 4> subResults;
  243. #endif
  244.         for (auto i(0u); i < subResults.size(); ++i)
  245.         {
  246.             auto subTile = thisData.getSubtile(i);
  247.             const int* sizeCache = thisSizeCache + 1 + i*subTile.num_subtiles();
  248.             int x = topLeftX + (i < 2 ? 0 : subTile.size());
  249.             int y = topLeftY + (i % 2 == 0 ? 0 : subTile.size());
  250.             // Search subtiles recursively.
  251. #ifdef NDEBUG
  252.             subResults[i] = async(searchForMaximum, masterData, masterSizeCache, subTile, sizeCache, x, y, radius, val);
  253. #else
  254.             subResults[i] = searchForMaximum(masterData, masterSizeCache, subTile, sizeCache, x, y, radius, val);
  255. #endif
  256.         }
  257.         // result stores ALL the best results.
  258.         vector<Result> result;
  259.         int currentBest = -1;
  260.         for (auto& fres : subResults)
  261.         {
  262.             auto res = fres.get();
  263.             if (res.front().value > currentBest)
  264.             {
  265.                 result = res;
  266.                 currentBest = res.front().value;
  267.             }
  268.             else if (res.front().value == currentBest)
  269.             {
  270.                 move(begin(res), end(res), back_inserter(result));
  271.             }
  272.         }
  273.         return result;
  274.     }
  275. }
  276.  
  277. #ifndef NDEBUG
  278. // Saves me typing/pasting/piping in test inputs all the time when debugging.
  279. istringstream input{
  280.     "4 4 2\n"
  281.     "....\n"
  282.     "....\n"
  283.     "..x.\n"
  284.     "....\n"
  285. };
  286. #define cin input
  287. #endif
  288.  
  289. int main()
  290. {
  291.     int height, width, radius;
  292.     cin >> height >> width >> radius;
  293.     cin.ignore();
  294.     int requiredSize = 1;
  295.     while (requiredSize < height || requiredSize < width)
  296.         requiredSize *= 2;
  297.  
  298.     // Create a tiled data buffer to hold the data
  299.     cout << "Building data buffer.\n";
  300.     vector<char> dataBuffer(TiledArray<char>::num_elements(requiredSize), ' ');
  301.     TiledArray<char> data(requiredSize,dataBuffer.data());
  302.  
  303. #ifndef NDEBUG
  304.     int nPlants = 0;
  305.     ofstream dataMirror{ "raw_data.txt" };
  306. #endif
  307.  
  308.     // Read data into the array.
  309.     cout << "Reading in data.\n";
  310.     for (int row(0); row < height; ++row)
  311.     {
  312.         string line;
  313.         getline(cin, line);
  314.         assert(line.size() >= static_cast<size_t>(width));
  315.         for (int col(0); col < width; ++col)
  316.         {
  317.             data.at(col, row) = line.at(col);
  318. #ifndef NDEBUG
  319.             if (line.at(col) == 'x')
  320.             {
  321.                 nPlants++;
  322.             }
  323. #endif
  324.         }
  325. #ifndef NDEBUG
  326.         dataMirror << line << '\n';
  327. #endif
  328.     }
  329. #ifndef NDEBUG
  330.     dataMirror << "Plants found: " << nPlants;
  331. #endif
  332.  
  333.     cout << "Creating a size cache.\n";
  334.     vector<int> sizeCache( data.num_subtiles(), -1 );
  335.     countElements(data, sizeCache.data(), 'x',sizeCache.data(),sizeCache.size());
  336.  
  337.     // Do the search
  338.     cout << "Searching...\n";
  339.     auto results = searchForMaximum(data, sizeCache.data(), data, sizeCache.data(), 0, 0, radius, 'x');
  340.  
  341.     // Output the results.
  342.     for (auto& res : results)
  343.     {
  344.         cout << "Sprinkler at (" << res.x << ',' << res.y << ") covers " << res.value << " plants.\n";
  345.        
  346.         // The map:
  347.  
  348.         for (int row(0); row < height; ++row)
  349.         {
  350.             for (int col(0); col < width; ++col)
  351.             {
  352.                 auto watered = isPointWithinCircle(col, row, { res.x, res.y, radius });
  353.                 auto plant = data.at(col, row) == 'x';
  354.                 auto isSprinkler = row == res.y && col == res.x;
  355.                 auto val = isSprinkler ? 'O' : (watered ? (plant ? '$' : '~') : (plant ? 'x' : '.'));
  356.                 cout << val;
  357.             }
  358.             cout << '\n';
  359.         }
  360.     }
  361. #ifndef NDEBUG
  362.     // Dumping some raw buffers to files allows me to check that the analysis was sane.
  363.     ofstream dataBufferFile{ "data_buffer.txt" };
  364.     dataBufferFile << "Address: 0x" << reinterpret_cast<void*>(dataBuffer.data())
  365.         << " size: " << dataBuffer.size() << '\n';
  366.  
  367.     copy(begin(dataBuffer), end(dataBuffer), ostream_iterator<char>(dataBufferFile));
  368.  
  369.     ofstream sizeCacheFile{ "size_cache.txt" };
  370.     sizeCacheFile << "Address: 0x" << reinterpret_cast<void*>(sizeCache.data())
  371.         << " size: " << sizeCache.size()*sizeof(int) << " (" << sizeCache.size() << ") elements." << '\n';
  372.     copy(begin(sizeCache), end(sizeCache), ostream_iterator<int>(sizeCacheFile, "\n"));
  373. #endif
  374. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement