কালারফাইটার - সি ++ - প্রাতঃরাশের জন্য কয়েকটা গিলে খায়
সম্পাদনা
- কোড পরিষ্কার
- একটি সহজ কিন্তু কার্যকর অপ্টিমাইজেশন যুক্ত
- কিছু জিআইএফ অ্যানিমেশন যুক্ত করেছে
Godশ্বর আমি সাপকে ঘৃণা করি (কেবল ভান করে তারা মাকড়সা, ইন্ডি)
আসলে আমি পাইথনকে ভালবাসি। আমি আশা করি আমি যদি অলস ছেলের চেয়ে কম থাকতাম এবং এটি সঠিকভাবে শিখতে শুরু করি তবেই এটি।
এই সমস্ত বলা হচ্ছে, বিচারককে কাজ পেতে আমাকে এই সাপের 64 বিট সংস্করণ দিয়ে লড়াই করতে হয়েছিল। উইন under এর অধীনে পাইথনের b৪ বিটের সংস্করণ দিয়ে পিআইএলকে কাজ করার জন্য আমি এই চ্যালেঞ্জকে উত্সর্গ করতে প্রস্তুত হওয়ার চেয়ে বেশি ধৈর্য প্রয়োজন, সুতরাং শেষ পর্যন্ত আমি উইন 32 সংস্করণে (যন্ত্রণাদায়ক) স্যুইচ করেছি।
এছাড়াও, যখন বট প্রতিক্রিয়া জানাতে খুব ধীর হয় তখন বিচারক খারাপভাবে ক্র্যাশ হয়।
পাইথন জ্ঞান না থাকায় আমি এটিকে ঠিক করিনি, তবে স্টিডিনে সময়সীমা শেষ করার পরে খালি উত্তরটি পড়তে হবে।
একটি ছোটখাট উন্নতি হ'ল প্রতিটি বোটের জন্য একটি ফাইলে স্ট্যাডার আউটপুট স্থাপন করা। এটি পোস্ট-মর্টেম ডিবাগিংয়ের জন্য ট্রেসিং সহজ করবে।
এই ছোটখাটো সমস্যা ব্যতীত আমি বিচারককে ব্যবহার করতে খুব সহজ এবং আনন্দদায়ক বলে মনে করি।
কুডোস আরও একটি উদ্ভাবনী এবং মজাদার চ্যালেঞ্জের জন্য।
কোড
#define _CRT_SECURE_NO_WARNINGS // prevents Microsoft from croaking about the safety of scanf. Since every rabid Russian hacker and his dog are welcome to try and overflow my buffers, I could not care less.
#include "lodepng.h"
#include <vector>
#include <deque>
#include <iostream>
#include <sstream>
#include <cassert> // paranoid android
#include <cstdint> // fixed size types
#include <algorithm> // min max
using namespace std;
// ============================================================================
// The less painful way I found to teach C++ how to handle png images
// ============================================================================
typedef unsigned tRGB;
#define RGB(r,g,b) (((r) << 16) | ((g) << 8) | (b))
class tRawImage {
public:
unsigned w, h;
tRawImage(unsigned w=0, unsigned h=0) : w(w), h(h), data(w*h * 4, 0) {}
void read(const char* filename) { unsigned res = lodepng::decode(data, w, h, filename); assert(!res); }
void write(const char * filename)
{
std::vector<unsigned char> png;
unsigned res = lodepng::encode(png, data, w, h, LCT_RGBA); assert(!res);
lodepng::save_file(png, filename);
}
tRGB get_pixel(int x, int y) const
{
size_t base = raw_index(x,y);
return RGB(data[base], data[base + 1], data[base + 2]);
}
void set_pixel(int x, int y, tRGB color)
{
size_t base = raw_index(x, y);
data[base+0] = (color >> 16) & 0xFF;
data[base+1] = (color >> 8) & 0xFF;
data[base+2] = (color >> 0) & 0xFF;
data[base+3] = 0xFF; // alpha
}
private:
vector<unsigned char> data;
void bound_check(unsigned x, unsigned y) const { assert(x < w && y < h); }
size_t raw_index(unsigned x, unsigned y) const { bound_check(x, y); return 4 * (y * w + x); }
};
// ============================================================================
// coordinates
// ============================================================================
typedef int16_t tCoord;
struct tPoint {
tCoord x, y;
tPoint operator+ (const tPoint & p) const { return { x + p.x, y + p.y }; }
};
typedef deque<tPoint> tPointList;
// ============================================================================
// command line and input parsing
// (in a nice airtight bag to contain the stench of C++ string handling)
// ============================================================================
enum tCommand {
c_quit,
c_update,
c_play,
};
class tParser {
public:
tRGB color;
tPointList points;
tRGB read_color(const char * s)
{
int r, g, b;
sscanf(s, "(%d,%d,%d)", &r, &g, &b);
return RGB(r, g, b);
}
tCommand command(void)
{
string line;
getline(cin, line);
string cmd = get_token(line);
points.clear();
if (cmd == "exit") return c_quit;
if (cmd == "pick") return c_play;
// even more convoluted and ugly than the LEFT$s and RIGHT$s of Apple ][ basic...
if (cmd != "colour")
{
cerr << "unknown command '" << cmd << "'\n";
exit(0);
}
assert(cmd == "colour");
color = read_color(get_token(line).c_str());
get_token(line); // skip "chose"
while (line != "")
{
string coords = get_token(line);
int x = atoi(get_token(coords, ',').c_str());
int y = atoi(coords.c_str());
points.push_back({ x, y });
}
return c_update;
}
private:
// even more verbose and inefficient than setting up an ADA rendezvous...
string get_token(string& s, char delimiter = ' ')
{
size_t pos = 0;
string token;
if ((pos = s.find(delimiter)) != string::npos)
{
token = s.substr(0, pos);
s.erase(0, pos + 1);
return token;
}
token = s; s.clear(); return token;
}
};
// ============================================================================
// pathing
// ============================================================================
class tPather {
public:
tPather(tRawImage image, tRGB own_color)
: arena(image)
, w(image.w)
, h(image.h)
, own_color(own_color)
, enemy_threat(false)
{
// extract colored pixels and own color areas
tPointList own_pixels;
color_plane[neutral].resize(w*h, false);
color_plane[enemies].resize(w*h, false);
for (size_t x = 0; x != w; x++)
for (size_t y = 0; y != h; y++)
{
tRGB color = image.get_pixel(x, y);
if (color == col_white) continue;
plane_set(neutral, x, y);
if (color == own_color) own_pixels.push_back({ x, y }); // fill the frontier with all points of our color
}
// compute initial frontier
for (tPoint pixel : own_pixels)
for (tPoint n : neighbour)
{
tPoint pos = pixel + n;
if (!in_picture(pos)) continue;
if (image.get_pixel(pos.x, pos.y) == col_white)
{
frontier.push_back(pixel);
break;
}
}
}
tPointList search(size_t pixels_required)
{
// flood fill the arena, starting from our current frontier
tPointList result;
tPlane closed;
static tCandidate pool[max_size*max_size]; // fastest possible garbage collection
size_t alloc;
static tCandidate* border[max_size*max_size]; // a FIFO that beats a deque anytime
size_t head, tail;
static vector<tDistance>distance(w*h); // distance map to be flooded
size_t filling_pixels = 0; // end of game optimization
get_more_results:
// ready the distance map for filling
distance.assign(w*h, distance_max);
// seed our flood fill with the frontier
alloc = head = tail = 0;
for (tPoint pos : frontier)
{
border[tail++] = new (&pool[alloc++]) tCandidate (pos);
}
// set already explored points
closed = color_plane[neutral]; // that's one huge copy
// add current result
for (tPoint pos : result)
{
border[tail++] = new (&pool[alloc++]) tCandidate(pos);
closed[raw_index(pos)] = true;
}
// let's floooooood!!!!
while (tail > head && pixels_required > filling_pixels)
{
tCandidate& candidate = *border[head++];
tDistance dist = candidate.distance;
distance[raw_index(candidate.pos)] = dist++;
for (tPoint n : neighbour)
{
tPoint pos = candidate.pos + n;
if (!in_picture (pos)) continue;
size_t index = raw_index(pos);
if (closed[index]) continue;
if (color_plane[enemies][index])
{
if (dist == (distance_initial + 1)) continue; // already near an enemy pixel
// reached the nearest enemy pixel
static tPoint trail[max_size * max_size / 2]; // dimensioned as a 1 pixel wide spiral across the whole map
size_t trail_size = 0;
// walk back toward the frontier
tPoint walker = candidate.pos;
tDistance cur_d = dist;
while (cur_d > distance_initial)
{
trail[trail_size++] = walker;
tPoint next_n;
for (tPoint n : neighbour)
{
tPoint next = walker + n;
if (!in_picture(next)) continue;
tDistance prev_d = distance[raw_index(next)];
if (prev_d < cur_d)
{
cur_d = prev_d;
next_n = n;
}
}
walker = walker + next_n;
}
// collect our precious new pixels
if (trail_size > 0)
{
while (trail_size > 0)
{
if (pixels_required-- == 0) return result; // ;!; <-- BRUTAL EXIT
tPoint pos = trail[--trail_size];
result.push_back (pos);
}
goto get_more_results; // I could have done a loop, but I did not bother to. Booooh!!!
}
continue;
}
// on to the next neighbour
closed[index] = true;
border[tail++] = new (&pool[alloc++]) tCandidate(pos, dist);
if (!enemy_threat) filling_pixels++;
}
}
// if all enemies have been surrounded, top up result with the first points of our flood fill
if (enemy_threat) enemy_threat = pixels_required == 0;
tPathIndex i = frontier.size() + result.size();
while (pixels_required--) result.push_back(pool[i++].pos);
return result;
}
// tidy up our map and frontier while other bots are thinking
void validate(tPointList moves)
{
// report new points
for (tPoint pos : moves)
{
frontier.push_back(pos);
color_plane[neutral][raw_index(pos)] = true;
}
// remove surrounded points from frontier
for (auto it = frontier.begin(); it != frontier.end();)
{
bool in_frontier = false;
for (tPoint n : neighbour)
{
tPoint pos = *it + n;
if (!in_picture(pos)) continue;
if (!(color_plane[neutral][raw_index(pos)] || color_plane[enemies][raw_index(pos)]))
{
in_frontier = true;
break;
}
}
if (!in_frontier) it = frontier.erase(it); else ++it; // the magic way of deleting an element without wrecking your iterator
}
}
// handle enemy move notifications
void update(tRGB color, tPointList points)
{
assert(color != own_color);
// plot enemy moves
enemy_threat = true;
for (tPoint p : points) plane_set(enemies, p);
// important optimization here :
/*
* Stop 1 pixel away from the enemy to avoid wasting moves in dogfights.
* Better let the enemy gain a few more pixels inside the surrounded region
* and use our precious moves to get closer to the next threat.
*/
for (tPoint p : points) for (tPoint n : neighbour) plane_set(enemies, p+n);
// if a new enemy is detected, gather its initial pixels
for (tRGB enemy : known_enemies) if (color == enemy) return;
known_enemies.push_back(color);
tPointList start_areas = scan_color(color);
for (tPoint p : start_areas) plane_set(enemies, p);
}
private:
typedef uint16_t tPathIndex;
typedef uint16_t tDistance;
static const tDistance distance_max = 0xFFFF;
static const tDistance distance_initial = 0;
struct tCandidate {
tPoint pos;
tDistance distance;
tCandidate(){} // must avoid doing anything in this constructor, or pathing will slow to a crawl
tCandidate(tPoint pos, tDistance distance = distance_initial) : pos(pos), distance(distance) {}
};
// neighbourhood of a pixel
static const tPoint neighbour[4];
// dimensions
tCoord w, h;
static const size_t max_size = 1000;
// colors lookup
const tRGB col_white = RGB(0xFF, 0xFF, 0xFF);
const tRGB col_black = RGB(0x00, 0x00, 0x00);
tRGB own_color;
const tRawImage arena;
tPointList scan_color(tRGB color)
{
tPointList res;
for (size_t x = 0; x != w; x++)
for (size_t y = 0; y != h; y++)
{
if (arena.get_pixel(x, y) == color) res.push_back({ x, y });
}
return res;
}
// color planes
typedef vector<bool> tPlane;
tPlane color_plane[2];
const size_t neutral = 0;
const size_t enemies = 1;
bool plane_get(size_t player, tPoint p) { return plane_get(player, p.x, p.y); }
bool plane_get(size_t player, size_t x, size_t y) { return in_picture(x, y) ? color_plane[player][raw_index(x, y)] : false; }
void plane_set(size_t player, tPoint p) { plane_set(player, p.x, p.y); }
void plane_set(size_t player, size_t x, size_t y) { if (in_picture(x, y)) color_plane[player][raw_index(x, y)] = true; }
bool in_picture(tPoint p) { return in_picture(p.x, p.y); }
bool in_picture(int x, int y) { return x >= 0 && x < w && y >= 0 && y < h; }
size_t raw_index(tPoint p) { return raw_index(p.x, p.y); }
size_t raw_index(size_t x, size_t y) { return y*w + x; }
// frontier
tPointList frontier;
// register enemies when they show up
vector<tRGB>known_enemies;
// end of game optimization
bool enemy_threat;
};
// small neighbourhood
const tPoint tPather::neighbour[4] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
// ============================================================================
// main class
// ============================================================================
class tGame {
public:
tGame(tRawImage image, tRGB color, size_t num_pixels)
: own_color(color)
, response_len(num_pixels)
, pather(image, color)
{}
void main_loop(void)
{
// grab an initial answer in case we're playing first
tPointList moves = pather.search(response_len);
for (;;)
{
ostringstream answer;
size_t num_points;
tPointList played;
switch (parser.command())
{
case c_quit:
return;
case c_play:
// play as many pixels as possible
if (moves.size() < response_len) moves = pather.search(response_len);
num_points = min(moves.size(), response_len);
for (size_t i = 0; i != num_points; i++)
{
answer << moves[0].x << ',' << moves[0].y;
if (i != num_points - 1) answer << ' '; // STL had more important things to do these last 30 years than implement an implode/explode feature, but you can write your own custom version with exception safety and in-place construction. It's a bit of work, but thanks to C++ inherent genericity you will be able to extend it to giraffes and hippos with a very manageable amount of code refactoring. It's not anyone's language, your C++, eh. Just try to implode hippos in Python. Hah!
played.push_back(moves[0]);
moves.pop_front();
}
cout << answer.str() << '\n';
// now that we managed to print a list of points to stdout, we just need to cleanup the mess
pather.validate(played);
break;
case c_update:
if (parser.color == own_color) continue; // hopefully we kept track of these already
pather.update(parser.color, parser.points);
moves = pather.search(response_len); // get cracking
break;
}
}
}
private:
tParser parser;
tRGB own_color;
size_t response_len;
tPather pather;
};
void main(int argc, char * argv[])
{
// process command line
tRawImage raw_image; raw_image.read (argv[1]);
tRGB my_color = tParser().read_color(argv[2]);
int num_pixels = atoi (argv[3]);
// init and run
tGame game (raw_image, my_color, num_pixels);
game.main_loop();
}
এক্সিকিউটেবল বিল্ডিং
আমি পিএনজি চিত্রগুলি পড়তে LODEpng.cpp এবং LODEpng.h ব্যবহার করেছি ।
অর্ধ ডজন লাইব্রেরি তৈরি না করে কীভাবে কোনও ছবি পড়তে হয় এই मंदির সি ++ ভাষা শেখানোর সবচেয়ে সহজ উপায় সম্পর্কে।
মূল এবং বব এর মামার সাথে LODEpng.cpp সংকলন করুন এবং লিংক দিন।
আমি এমএসভিসি ২০১৩3 দিয়ে সংকলন করেছি, তবে যেহেতু আমি কেবল কয়েকটি এসটিএল বেসিক পাত্রে (ডেক এবং ভেক্টর) ব্যবহার করেছি, এটি জিসিসি দিয়ে কাজ করতে পারে (যদি আপনি ভাগ্যবান হন)।
যদি এটি না হয় তবে আমি একটি মিনিজিডব্লু বিল্ড চেষ্টা করতে পারি , তবে সত্যি বলতে আমি সি ++ বহনযোগ্যতার সমস্যা নিয়ে ক্লান্ত হয়ে পড়ছি।
আমি আমার দিনগুলিতে প্রচুর বহনযোগ্য সি / সি ++ করেছি (বিভিন্ন 8 থেকে 32 বিট প্রসেসরের পাশাপাশি সানস, উইন্ডোজ 3.11 থেকে ভিস্টা এবং লিনাক্স এর বাল্যকাল থেকে উবুন্টু কুইং জেব্রা বা যে কোনও কিছুতে বিদেশী সংকলকগুলিতে, তাই আমি মনে করি পোর্টেবিলিটিটির অর্থ কী তা সম্পর্কে আমার বেশ ভাল ধারণা আছে) তবে এসটিএল দানবটির ক্রিপ্টিক এবং ফোলাযুক্ত স্পেসের জিএনইউ এবং মাইক্রোসফ্টের ব্যাখ্যাগুলির মধ্যে অগণিত তাত্পর্যগুলি মুখস্থ করার প্রয়োজন ছিল না (বা আবিষ্কার করতে হবে)।
সোয়িলারের বিরুদ্ধে ফলাফল
কিভাবে এটা কাজ করে
মূলত, এটি একটি সাধারণ ব্রুট-ফোর্স ফিলিফিল পাথিং।
প্লেয়ারের রঙের সীমান্ত (অর্থাত পিক্সেলগুলিতে কমপক্ষে একটি সাদা প্রতিবেশী রয়েছে) ক্লাসিক দূরত্ব-বন্যার অ্যালগরিদম সম্পাদন করতে বীজ হিসাবে ব্যবহৃত হয়।
যখন কোনও বিন্দু শত্রু রঙের সান্নিধ্যে পৌঁছে, একটি পশ্চাদপথের পথটিকে নিকটতম শত্রু স্পেসের দিকে অগ্রসর হয়ে পিক্সেলের একটি স্ট্রিং উত্পাদন করতে গণনা করা হয়।
পছন্দসই দৈর্ঘ্যের প্রতিক্রিয়ার জন্য পর্যাপ্ত পয়েন্ট সংগ্রহ না করা অবধি প্রক্রিয়াটি পুনরাবৃত্তি হবে।
এই পুনরাবৃত্তি অশ্লীল ব্যয়বহুল, বিশেষত যখন শত্রুর কাছে লড়াই করে।
প্রতিবার সীমানা থেকে শত্রু পিক্সেলের দিকে যাওয়ার পিক্সেলের একটি স্ট্রিং পাওয়া গেছে (এবং উত্তরটি পূরণ করার জন্য আমাদের আরও পয়েন্ট দরকার), সীমান্তে নতুন পথ যুক্ত হওয়ার সাথে সাথে বন্যার ভরাট শুরু থেকে আবার করা হয়। এর অর্থ 10 পিক্সেলের উত্তর পেতে আপনাকে 5 টি বন্যার ভরাট বা আরও কিছু করতে হবে।
আর শত্রু পিক্সেলগুলি যদি না পাওয়া যায় তবে সীমা পিক্সেলের স্বেচ্ছাসেবীরা বেছে নেওয়া হয়।
অ্যালগরিদম বরং একটি অদক্ষ বন্যার ভরাটকে রূপান্তরিত করে, তবে এটি কেবলমাত্র गेमটির ফলাফল সিদ্ধান্ত নেওয়ার পরে ঘটে (অর্থাত্ যুদ্ধ করার জন্য আরও নিরপেক্ষ অঞ্চল নেই)।
আমি এটিকে অপ্টিমাইজ করেছিলাম যাতে প্রতিযোগিতাটি মোকাবেলা করার পরে বিচারক মানচিত্রে পূর্ণ বয়সে ব্যয় করবেন না। বর্তমান অবস্থায়, মৃত্যুদণ্ড কার্যকর করার সময়টি বিচারকের সাথে তুলনায় অবহেলিত।
যেহেতু শত্রুর রং শুরুতে জানা যায় না, তাই শত্রুর শুরুর দিকের অঞ্চলগুলি যখন এটি প্রথম স্থানান্তরিত করে তখন অনুলিপি করার জন্য প্রাথমিক অঙ্গনের চিত্রটি সংরক্ষণ করা হয়।
কোডটি যদি প্রথমে বাজায় তবে তা কেবল কয়েকটা স্বেচ্ছাসেবক পিক্সেল ভরে যাবে।
এটি অ্যালগরিদমকে শত্রুদের একটি স্বেচ্ছাসেবী সংখ্যার বিরুদ্ধে লড়াই করতে সক্ষম করে তোলে এবং এমনকি নতুন শত্রুরা সময় মতো এলোমেলো পয়েন্টে এসে পৌঁছায়, বা রঙগুলি কোনও প্রারম্ভিক অঞ্চল ছাড়াই উপস্থিত হয় (যদিও এর একেবারেই ব্যবহারিক ব্যবহার নেই)।
রঙ-প্রতি-রঙের ভিত্তিতে শত্রু হ্যান্ডলিং বট সহযোগিতা (পিক্সেল স্থানাঙ্ক ব্যবহার করে একটি গোপন স্বীকৃতি স্বাক্ষর পাস করার জন্য) দুটি উদাহরণও পেতে পারে।
মজার মত শোনাচ্ছে, আমি সম্ভবত চেষ্টা করব :)।
নতুন ডেটা উপলব্ধ হওয়ার সাথে সাথে গণনা-ভারী পাথিং করা হয় (একটি সরানোর বিজ্ঞপ্তির পরে), এবং কিছু অপ্টিমাইজেশন (সীমান্ত আপডেট) একটি প্রতিক্রিয়া দেওয়ার ঠিক পরে সম্পন্ন করা হয়েছে (অন্যান্য বট মোড়ের সময় যতটা সম্ভব গণনা করা )।
এখানে আবার 1 টিরও বেশি শত্রু থাকলে আরও সূক্ষ্ম কাজ করার উপায় থাকতে পারে (নতুন ডেটা উপলব্ধ হলে কোনও গণনা বাতিল করা) তবে যে কোনও হারে আমি দেখতে ব্যর্থ হলাম যেখানে মাল্টিটাস্কিং দরকার, যতক্ষণ না অ্যালগরিদম হয় সম্পূর্ণ বোঝা উপর কাজ করতে সক্ষম।
পারফরম্যান্স সমস্যা
এই সমস্ত দ্রুত ডেটা অ্যাক্সেস ব্যতীত কাজ করতে পারে না (এবং পুরো অ্যাপোলো প্রোগ্রামের তুলনায় আরও বেশি কম্পিউটিং শক্তি, যেমন আপনার টুইট পিসি যখন কিছু টুইট পোস্ট করার চেয়ে বেশি কিছু করত)।
গতি ভারী সংকলন-নির্ভর। সাধারণত জিএনইউ মাইক্রোসফ্টকে 30% ব্যবধানে পরাজিত করে (আমি 3 টি পাথিং সম্পর্কিত কোডের চ্যালেঞ্জগুলিতে লক্ষ্য করেছি এমন ম্যাজিক নম্বর) তবে এই মাইলেজটি অবশ্যই আলাদা হতে পারে।
কোডটি তার বর্তমান অবস্থায় সবেমাত্র আখরায়ের ঘাম ভেঙে 4.. উইন্ডোজ পারফিউটারটি 4 থেকে 7% সিপিইউ ব্যবহারের বিষয়ে রিপোর্ট করে, সুতরাং এটি 100 মিমি প্রতিক্রিয়া সময়সীমার মধ্যে 1000x1000 মানচিত্রের সাথে মোকাবিলা করতে সক্ষম হওয়া উচিত।
প্রায় প্রতিটি পথের অ্যালগরিদমের হৃদয়ে একটি ফিফো থাকে (সম্ভবত ছদ্মবেশী, যদিও সে ক্ষেত্রে নয়), যার পরিবর্তে দ্রুত উপাদান বরাদ্দ প্রয়োজন।
যেহেতু ওপি বাধ্যতামূলকভাবে আখড়ার আকারের সীমা নির্ধারণ করেছে, তাই আমি কিছু গণিতও করেছি এবং দেখেছি যে স্থির ডাটা স্ট্রাকচার সর্বাধিক (অর্থাৎ 1.000.000 পিক্সেল) মাত্রিক কয়েক ডজন মেগাবাইটের বেশি গ্রহণ করবে না, যা আপনার গড় পিসি প্রাতঃরাশের জন্য খায়।
প্রকৃতপক্ষে উইন under এর অধীনে এবং এমএসভিসি ২০১৩ এর সাথে সংকলিত, কোডটি আখরোট 4 তে প্রায় 14 এমবি খরচ করে, যখন সুইলারের দুটি থ্রেড 20Mb এর বেশি ব্যবহার করছে।
সহজ প্রোটোটাইপিংয়ের জন্য আমি এসটিএল ধারকগুলি দিয়ে শুরু করেছি, তবে এসটিএল কোডটি আরও কম পঠনযোগ্য করে তুলেছে, যেহেতু আমার বাধাগ্রস্থতা দূরে রাখতে প্রতিটি তথ্য বিস্তৃত করার জন্য কোনও শ্রেণি তৈরি করার ইচ্ছা ছিল না (এটি আমার নিজের অক্ষমতার কারণে কিনা এসটিএল সহ্য করা পাঠকের প্রশংসা ছেড়ে যায়)।
নির্বিশেষে, ফলাফলটি এতটা নৃশংসভাবে ধীর হয়েছিল যে প্রথমে আমি ভেবেছিলাম আমি ভুল করে একটি ডিবাগ সংস্করণ তৈরি করছি।
আমি মনে করি এটি আংশিকভাবে এসটিএলটির অবিশ্বাস্যরূপে দুর্বল মাইক্রোসফ্ট বাস্তবায়নের কারণে হয়েছে (যেখানে উদাহরণস্বরূপ, ভেক্টর এবং বিটসেটগুলি অপারেটরের উপর আবদ্ধ চেক বা অন্যান্য ক্রিপিক অপারেশন করে [], স্পেকটির সরাসরি লঙ্ঘন করে), এবং আংশিকভাবে এসটিএল ডিজাইনের নিজেই।
পারফরম্যান্সগুলি উপস্থিত থাকলে আমি নৃশংস সিনট্যাক্স এবং বহনযোগ্যতা (অর্থাত্ মাইক্রোসফ্ট বনাম জিএনইউ) সমস্যাগুলি মোকাবিলা করতে পারি, তবে অবশ্যই এটি ঘটেনি।
উদাহরণস্বরূপ, deque
সহজাতভাবে ধীর, কারণ এটি প্রচুর বুককিপিংয়ের ডেটাগুলি তার অতি স্মার্ট রিসাইজিংয়ের জন্য অপেক্ষা করার আশেপাশে অপেক্ষা করে, যা সম্পর্কে আমি কম যত্ন নিতে পারি না।
নিশ্চিত যে আমি একটি কাস্টম বরাদ্দকারী এবং তীর্থযন্ত্রের অন্যান্য কাস্টম টেম্পলেট বিটগুলি প্রয়োগ করতে পারতাম তবে কাস্টম বরাদ্দকারীর একাই কয়েকশ লাইন কোড এবং এক দিনের আরও ভাল অংশ পরীক্ষা করতে হয়, এটি প্রয়োগ করতে হবে এমন কয়েক ডজন ইন্টারফেসের সাথে কী হয়? হস্তনির্মিত সমতুল্য কাঠামো কোডের প্রায় শূন্যরেখার (যদিও আরও বিপজ্জনক, তবে আমি যদি না জানতাম - তবে মনে করি আমি জানি না - আমি যেভাবেই করছিলাম) তবে অ্যালগরিদমটি কাজ করত না।
সুতরাং শেষ পর্যন্ত আমি এসটিএল কনটেইনারগুলি কোডের অ-সমালোচিত অংশগুলিতে রেখে দিয়েছিলাম এবং আমার নিজের পাশবিক বরাদ্দকারী এবং ফিফো দুটি দুটি সার্কা 1970 টি অ্যারে এবং তিনটি স্বাক্ষরবিহীন শর্টস দিয়ে তৈরি করেছি।
গিলে গিলে ফেলা
যেমনটি এর লেখক নিশ্চিত করেছেন যে শত্রু মুভ নোটিফিকেশন এবং পথের থ্রেডের আপডেটের মধ্যে ব্যবধানের কারণে সোয়িলার এর অনিচ্ছাকৃত নিদর্শনগুলি ঘটে।
সিস্টেম পারফিউটারটি স্পষ্টভাবে দেখায় যে সমস্ত সময় 100% সিপিইউ গ্রহণ করে এবং জ্যাজড নিদর্শনগুলি উপস্থিত হয় যখন লড়াইয়ের ফোকাসটি কোনও নতুন অঞ্চলে স্থানান্তরিত হয়। এটি অ্যানিমেশনগুলির সাথেও বেশ স্পষ্ট।
একটি সহজ তবে কার্যকর অপ্টিমাইজেশন
গ্রাহক এবং আমার যোদ্ধার মধ্যে মহাকাব্য ডগফাইটগুলি দেখার পরে, আমি গো খেলার একটি পুরানো কথা স্মরণ করেছি: কাছ থেকে রক্ষা করুন, কিন্তু দূর থেকে আক্রমণ করুন।
জ্ঞান আছে। যদি আপনি আপনার শত্রুদের খুব বেশি লেগে থাকার চেষ্টা করেন তবে আপনি প্রতিটি সম্ভাব্য পথ অবরুদ্ধ করার চেষ্টা করে মূল্যবান পদক্ষেপগুলি নষ্ট করবেন। বিপরীতে, আপনি যদি মাত্র এক পিক্সেল দূরে থাকেন তবে আপনি সম্ভবত সামান্য ফাঁকা জায়গা পূরণ করা এড়াতে পারবেন এবং আরও গুরুত্বপূর্ণ হুমকির মোকাবেলায় আপনার পদক্ষেপগুলি ব্যবহার করবেন।
এই ধারণাটি বাস্তবায়নের জন্য, আমি কেবল শত্রুটির চালগুলি প্রসারিত করেছি (প্রতিটি পদক্ষেপের 4 প্রতিবেশীকে শত্রু পিক্সেল হিসাবে চিহ্নিত করে)।
এটি শত্রুর সীমানা থেকে এক পিক্সেল দূরে পথের অ্যালগরিদমকে থামিয়ে দেয়, আমার যোদ্ধাকে খুব বেশি ডগফাইটে না জড়ালে একটি প্রতিপক্ষের আশেপাশে কাজ করতে দেয়।
আপনি উন্নতি দেখতে পাচ্ছেন
(যদিও সমস্ত রান তত সফল নয়, আপনি আরও মসৃণ রূপরেখা লক্ষ্য করতে পারেন):