Kirov Reporting !

เมื่อเกมคอมพิวเตอร์ เจอกับการจำลองสงครามบนกระดาษ อนาคตจึงบังเกิด...
4 กุมภาพันธ์ 2026เวลาอ่าน 6 นาที#devops#softwareengineer#gis

เคยได้ยินคำว่า War Game กันมั้ยครับ ถ้าผมพูดคำว่า War Game หรือ War Gaming เชื่อว่าหลายคนในยุค 90s (รวมถึงผมด้วย) น่าจะมีเสียงในหัวดังขึ้นมาทันทีว่า "Kirov Reporting!" หรือ "Construction Complete" จากเกมตระกูล C&C หรือ Red Alert ใช่ไหมครับ? 555

แต่วันนี้ผมจะพาคุณออกจากโลกของ RTS (Real-Time Strategy) แบบที่เราคลิกเมาส์รัวๆ เพื่อปั๊มรถถัง มาสู่โลกจริงของ Military Operation Simulator หรือระบบจำลองยุทธ์ของจริงที่ผมเคยมีโอกาสได้พัฒนาขึ้นมาครับ ถึงมันจะเป็นแค่เดโมไม่เคยใช้งานจริง (อีกแล้ว) ก็เถอะ บอกเลยว่าเบื้องหลังมันไม่ได้มีแค่เรื่อง ขุดแร่ ปั๊มเงิน สร้างรถถังเพื่อทำลายฐานศัตรู แต่มันคือการบริหารจัดการ Data & Logic ภายใต้ความกดดันสูง และมันไม่ใช่การที่จะคลิ๊กเม้าส์เพื่อเลื่อน Unit แต่จะเป็นการใส่ข้อมูลกำลังรบ ข้อมูลการตัดสินใจ (Decisive Point) และอื่นๆ อีกมากมาย จากนั้นกดปุ่ม ปั้ง! ให้เป็นโกโก้ครั้นช์ และดูผลลัพธ์

War Gaming Example

ทำไมต้องรบในกระดาษ (The Art of War Gaming)

ก่อนจะมีคอมพิวเตอร์ การวางแผนการรบหรือทดสอบแผนการรบไม่ได้ทำกันในสนามจริงครับ อันนั้นคือการที่เอาไปซ้อมรบแบบ Field Exercise แล้ว แต่ขั้นตอนวางแผนมันต้องทำแบบจำลองขึ้นมาเพราะมันสิ้นเปลืองงบประมาณและเสี่ยงอันตราย เขาจึงทำกันบนกระดาษ หรือ โต๊ะทราย คือ "โต๊ะที่มีทราย จริงๆ นะ"

ในอดีต (และปัจจุบันก็ยังใช้อยู่) เหล่าเสนาธิการเขาจะกางแผนที่ขนาดใหญ่ แล้วใช้เบี้ยหรือบล็อกไม้แทนกองพัน กองร้อย เลื่อนไปมาเพื่อจำลองสถานการณ์ นี่คือจุดเริ่มต้นของ War Gaming ครับ เป้าหมายไม่ใช่ความมันส์ แต่คือการฝึก กระบวนการตัดสินใจ (Decision Making) ซึ่งถ้าเทียบกับโลก Software มันก็เหมือนกับการทำ Unit Test หรือ Integration Test ให้กับแผนการรบ ก่อนจะเอาไป Deploy จริงในสนามนั่นเองครับ

ถ้าไม่มี Computer Simulation ละก็นะ

ใน War Game แบบ Manual มนุษย์เรามีสิ่งที่เรียกว่า Bias (ความลำเอียง) ครับ เช่น กองพันนี้ฉันฝึกมาดี มันต้องยิงแม่นสิ หรือกรรมการตัดสินพลาดเพราะคำนวณ Damage ผิด เพราะ Unit หรือ หน่วยที่ทำการรบ (เช่น เรือ) มันจะมีสิ่งที่เรียกว่า Salvo Size มาคำนวนว่าต้องทำลายด้วยอาวุธอะไร จำนวนเท่าไหร่ ถึงจะทำให้สิ้นสภาพ (Neutralize) ได้ ซึ่งพอเรานำ Computer Simulation เข้ามาช่วย คอมพิวเตอร์มันไม่มีคำว่าลำเอียงครับ มันทำงานตาม Algorithm และ Random Number Generator (RNG) ที่อิงตามหลักสถิติล้วนๆ อย่าง

  • ระยะยิง คำนวณตามจริงจากพิกัด GIS
  • ภูมิประเทศ (Terrain) มี Line of Sight (LoS) บังหรือไม่
  • สภาพอากาศ ลมแรงแค่ไหน ความชื้นเท่าไหร่ ซึ่งมีผลต่อขีปนวิถีของอาวุธ (ศัพท์งงไปมั้ย ทิศทางการยิงน่ะแหละ)
  • และอื่นๆ ที่มีผลต่อการเอาไปคำนวณ ณ ขณะนั้น การใช้คอมพิวเตอร์ช่วยขจัด Human Error ทำให้เสนาธิการสามารถโฟกัสที่กลยุทธ์ได้เต็มที่ โดยไม่ต้องเสียเวลาเถียงกับกรรมการครับ
พระเอกก็คือ Data

ถ้าได้ติดตามสไตล์ของ CodeIndy แล้ว ผมจะให้ความสำคัญกับ Data Integrity เป็นอย่างมาก ซึ่งใน Simulator นั้น ข้อมูลที่ป้อนเข้าจะเป็นข้อมูลที่เรียกว่า IPOE (Intelligence Preparation of the Operational Environment) มันคือข้อมูลการข่าวของในสนามรบน่ะ ซึ่งเพราะเราไม่ได้แค่กำหนดพลังโจมตี 1-10 เหมือนในเกม แต่เราต้องคำนวณเปรียบเทียบกันว่ากำลังในภาพรวมของแต่ละฝ่ายต่างกันอย่างไรบ้าง หรือจะเรียกแบบทหารๆ หน่อยว่า "การเปรียบเทียบกำลังรบสัมพัทธ์" จะมี

  • Logistics กระสุนเหลือเท่าไหร่? น้ำมันพอไหม? (ใน Simulator ถ้า Logistic ขาด ยูนิตนั้นก็แค่เป้านิ่ง พร้อมชิ่ง โคตรซิ่ง ไม่รู้จริงๆ อยู่นิ่งๆ ไม่ได้)
  • GIS Data ข้อมูลแผนที่ต้องเป็นชั้นข้อมูลจริง ไม่ใช่แค่ภาพสวยงาม เพื่อใช้คำนวณเส้นทางการเดินของ Unit ตามความลาดชัน จุดที่ท้าทายที่สุดคือการทำ Interoperability หรือการทำให้ระบบ Simulation สามารถคุยกับระบบอื่นได้ (เช่น ระบบ C2) ซึ่งเรามักจะใช้มาตรฐานอย่าง HLA (High Level Architecture) เพื่อให้แน่ใจว่าข้อมูลการรบ ไหลเวียนได้อย่างเป็นระบบ แต่ก็ต้องแยกกับข้อมูลของสถานการณ์จริงจาก C2
เอาหละ มาถึง Tech Stack กัลลล

คราวนี้ถอดหมวกเสนาธิการ ซึ่งเป็นส่วนผู้ใช้งานระบบ Simulator นี้ มาสวมหมวก #softwareengineer กันเลยวัยรุ่น โจทย์หินก็คือ จำลองยูนิตนับหมื่น บนแผนที่จริง แบบ 3D และต้อง Real-time เราจัดการมันยังไงดี ซึ่งก็แน่นอนว่าถ้าใครมีความคุ้นเคยกับการพัฒนาเกมแบบ MMORPG บ้าง ก็จะเข้าใจเรื่องการบริหารจัดการ Session บน Server เพื่อให้ผู้เล่นสามารถเล่นร่วมกันได้แบบ Real-time ทีละหลายๆ คน ยิ่งโหมดแบบตี Guild นี่ใช้เลย แต่ที่แตกต่างกันก็คือทุกอย่างจะต้องเสมือนจริง ซึ่งถ้าเกมทั่วไปมันเหมือนจริงกันไปมันก็จะไม่สนุก และแน่นอน War Gaming มันไม่ได้สนุกครับ

ส่วน Frontend & Rendering ที่ผมเคยใช้จะเป็น threejs เป็นแกนหลัก ซึ่งเมื่อก่อนจะใช้เป็น WebGL2 ในการ Render แผนที่บนเว็บ แต่ปัจจุบันก็พยายาม port มาเป็น WebGPU เพื่อใช้ Compute Shader ในการคำนวณ Physics ของกระสุนและวิถีโค้งนับพันชุดบน GPU โดยไม่ทำให้ UI กระตุกได้ด้วย

javascript
// อันนี้เป็นส่วนหนึ่งของการ render แผนที่หลังจาก pan/tilt/zoom

const TILE_SIZE = 256;
const loader = new THREE.TextureLoader();
const textureMap = {};

const draw = async function(z, x, y, originX, originY, tileSize) {
  const material = new THREE.MeshStandardMaterial({color:0x000000});
  const geometry = new THREE.PlaneGeometry(tileSize, tileSize, 1, 1);
  const mesh = new THREE.Mesh(geometry, material);
  mesh.precision = 'highp';
  mesh.position.set(originX, originY, 0);
  drawImage(material, z, x, y);
  return mesh;
};

const drawImage = async function(material, z, x, y) {
  if(textureMap[z] == undefined) textureMap[z] = {};
  if(textureMap[z][x] == undefined) textureMap[z][x] = {};
  if(textureMap[z][x][y]) {
    material.color.set(0xFFFFFF);
    material.map = textureMap[z][x][y];
    material.needsUpdate = true;
  } else {
    drawTempolary(z, x, y).then(async function(texture) {
      if(texture) {
        material.color.set(0xFFFFFF);
        material.map = texture;
        material.needsUpdate = true;
        textureMap[z][x][y] = texture;
      }
    });
    const url = `https://maptile.sample.com?z=${z}&x=${x}&y=${y}`;
    loader.load(url, async function(texture) {
      texture.wrapS = THREE.ClampToEdgeWrapping;
      texture.wrapT = THREE.ClampToEdgeWrapping;
      material.color.set(0xFFFFFF);
      material.map = texture;
      material.needsUpdate = true;
      textureMap[z][x][y] = texture;
    });
  }
};

const drawTempolary = async function(z, x, y) {
  for(let i=1; i<=z; i++) {
    const zz = z - i;
    const scale = 2**(2**(i - 1));
    const xx = Math.floor(x/scale);
    const yy = Math.floor(y/scale);
    if(textureMap[zz] == undefined) return;
    if(textureMap[zz][xx] == undefined) return;
    const map = textureMap[zz][xx][yy];
    if(map) {
      const tileSize = TILE_SIZE/scale;
      const cx = -tileSize*(xx*scale - x);
      const cy = -tileSize*(yy*scale - y);
      const canvas = document.createElement('canvas');
      canvas.width = TILE_SIZE;
      canvas.height = TILE_SIZE;
      const context = canvas.getContext('2d');
      context.drawImage(map.image, cx, cy, tileSize, tileSize, 0, 0, TILE_SIZE, TILE_SIZE);
      const texture = new THREE.CanvasTexture(canvas)
      texture.wrapS = THREE.ClampToEdgeWrapping;
      texture.wrapT = THREE.ClampToEdgeWrapping;
      return texture;
    }
  }
};

การเก็บข้อมูล โดยเฉพาะส่วนของ Spatial Database ที่ผมใช้และปัจจุบันก็ยัง Powerful อยู่ระดับหนึ่งก็คือ PostgreSQL + PostGIS ในการเก็บข้อมูลเชิงพื้นที่ทั้งหมด การหาว่าใครมองเห็นใครได้บ้าง (Line of Sight) เราจะผลักภาระบางส่วนไปให้ Service ที่ทำ Cache ไว้จาก Database คำนวณผ่าน Spatial Query เพื่อประสิทธิภาพสูงสุด

ส่วนของ Real-time State Management เมื่อยูนิตมีการขยับขยับหรือทำอะไรบางอย่าง State ต้อง Sync ถึงกันหมด ซึ่งสามารถใช้ได้หลายเครื่องมือ แต่สำหรับผมแล้ว Redis จะง่ายที่สุด ซึ่งเป็น Pub/Sub คอย Broadcast ตำแหน่งยูนิตผ่าน Websocket ไปยัง Client ทุกเครื่อง

ที่สำคัญ การ Scalability ระบบต้องรันบน Kubernetes (K8s) เพื่อรองรับช่วงที่เกิดการปะทะหนักๆ (Heavy Load) โดยมี Nginx คอยทำ Load Balancing และจัดการ Traffic ให้ไหลลื่น

บทสรุป ตัวเชื่อมจากการวางแผน สู่ชัยชนะ

การสร้าง War Game Simulator ไม่ใช่แค่การเขียนเกมให้สนุก ไม่ใช่การทำ Algorithm ที่หน้าจอดำๆ เขียวๆ และให้ผลออกมา แต่มันคือการสร้างเครื่องมือตัดสินใจที่ Balance และต้องอาศัยทั้งความแม่นยำของ Data ประสิทธิภาพของ Software Stack และการออกแบบที่แม่นยำ แต่ก็ต้องยืดหยุ่น ซึ่งเป็นอีกหนึ่งงานที่จะต้องมีตัวเชื่อมระหว่างความต้องการของผู้ใช้งาน กับการพัฒนาทางด้านเทคนิค ถ้าคุยกันไม่รู้เรื่อง เละฮะ บอกเลย