์šฐ๋ฆฌ๋Š” Dota 2014 ๋งค์น˜๋ฉ”์ดํ‚น์„ ์ž‘์„ฑ ์ค‘์ž…๋‹ˆ๋‹ค.

์•ˆ๋…• ๋ชจ๋‘

์ด๋ฒˆ ๋ด„์— ์ €๋Š” ์‚ฌ๋žŒ๋“ค์ด Dota 2 ์„œ๋ฒ„ ๋ฒ„์ „ 2014๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šฐ๊ณ  ์ด์— ๋”ฐ๋ผ ํ”Œ๋ ˆ์ดํ•˜๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์ ‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด ๊ฒŒ์ž„์˜ ์—ด๋ ฌํ•œ ํŒฌ์ด๊ณ , ์–ด๋ฆฐ ์‹œ์ ˆ์— ๋ชฐ์ž…ํ•  ์ˆ˜ ์žˆ๋Š” ์ด ๋…ํŠนํ•œ ๊ธฐํšŒ๋ฅผ ๋†“์น  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

์ €๋Š” ๋งค์šฐ ๊นŠ์ด ํŒŒ๊ณ ๋“ค์—ˆ๊ณ , ์ด์ „ ๋ฒ„์ „์˜ ๊ฒŒ์ž„์—์„œ ์ง€์›๋˜์ง€ ์•Š๋Š” ๊ฑฐ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ, ์ฆ‰ ๋งค์น˜๋ฉ”์ดํ‚น์„ ๋‹ด๋‹นํ•˜๋Š” Discord ๋ด‡์„ ์ž‘์„ฑํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
๋ด‡์„ ํ†ตํ•œ ๋ชจ๋“  ํ˜์‹  ์ด์ „์—๋Š” ๋กœ๋น„๊ฐ€ ์ˆ˜๋™์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€์— ๋Œ€ํ•œ 10๊ฐœ์˜ ๋ฐ˜์‘์„ ์ˆ˜์ง‘ํ•˜๊ณ  ์ˆ˜๋™์œผ๋กœ ์„œ๋ฒ„๋ฅผ ๊ตฌ์„ฑํ•˜๊ฑฐ๋‚˜ ๋กœ์ปฌ ๋กœ๋น„๋ฅผ ํ˜ธ์ŠคํŒ…ํ–ˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” Dota 2014 ๋งค์น˜๋ฉ”์ดํ‚น์„ ์ž‘์„ฑ ์ค‘์ž…๋‹ˆ๋‹ค.

ํ”„๋กœ๊ทธ๋ž˜๋จธ๋กœ์„œ์˜ ๋‚ด ์„ฑ๊ฒฉ์€ ๊ทธ๋ ‡๊ฒŒ ๋งŽ์€ ์ˆ˜์ž‘์—…์„ ๊ฒฌ๋”œ ์ˆ˜ ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋ฃป๋ฐค ์‚ฌ์ด์— 10๋ช…์ด ๋˜๋ฉด ์ž๋™์œผ๋กœ ์„œ๋ฒ„๋ฅผ ์˜ฌ๋ฆฌ๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฒ„์ „์˜ ๋ด‡์„ ์Šค์ผ€์น˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” Python์„ ๋ณ„๋กœ ์ข‹์•„ํ•˜์ง€ ์•Š๊ณ  ์ด ํ™˜๊ฒฝ์ด ๋” ํŽธ์•ˆํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฆ‰์‹œ nodejs๋กœ ์ž‘์„ฑํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

Discord์šฉ ๋ด‡์„ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์€ ์ด๋ฒˆ์ด ์ฒ˜์Œ์ด์ง€๋งŒ ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋‹ค๋Š” ๊ฒƒ์ด ๋ฐํ˜€์กŒ์Šต๋‹ˆ๋‹ค. ๊ณต์‹ npm ๋ชจ๋“ˆ discord.js๋Š” ๋ฉ”์‹œ์ง€ ์ž‘์—…, ๋ฐ˜์‘ ์ˆ˜์ง‘ ๋“ฑ์„ ์œ„ํ•œ ํŽธ๋ฆฌํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋ฉด์ฑ…์กฐํ•ญ: ๋ชจ๋“  ์ฝ”๋“œ ์˜ˆ์ œ๋Š” "ํ˜„์žฌ"์ž…๋‹ˆ๋‹ค. ์ฆ‰, ๋ฐค์— ์—ฌ๋Ÿฌ ๋ฒˆ ์žฌ์ž‘์„ฑ์„ ๊ฑฐ์ณค์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๋งค์น˜๋ฉ”์ดํ‚น์˜ ๊ธฐ๋ณธ์€ ํ”Œ๋ ˆ์ด๋ฅผ ์›ํ•˜๋Š” ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๊ฒŒ์ž„์„ ์›ํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ๊ฒŒ์ž„์„ ์ฐพ์ง€ ์•Š์„ ๋•Œ ๋ฐฐ์น˜๋˜๊ณ  ์ œ๊ฑฐ๋˜๋Š” "๋Œ€๊ธฐ์—ด"์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๋ฐ”๋กœ 'ํ”Œ๋ ˆ์ด์–ด'์˜ ๋ณธ์งˆ์ž…๋‹ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” Discord์˜ ์‚ฌ์šฉ์ž ID์˜€์ง€๋งŒ ์‚ฌ์ดํŠธ์—์„œ ๊ฒŒ์ž„์„ ์‹œ์ž‘/๊ฒ€์ƒ‰ํ•  ๊ณ„ํš์ด ์žˆ์ง€๋งŒ ๋จผ์ € ํ•ด์•ผ ํ•  ์ผ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

export enum Realm {
  DISCORD,
  EXTERNAL,
}

export default class QueuePlayer {
  constructor(public readonly realm: Realm, public readonly id: string) {}

  public is(qp: QueuePlayer): boolean {
    return this.realm === qp.realm && this.id === qp.id;
  }

  static Discord(id: string) {
    return new QueuePlayer(Realm.DISCORD, id);
  }

  static External(id: string) {
    return new QueuePlayer(Realm.EXTERNAL, id);
  }
}

๊ทธ๋ฆฌ๊ณ  ์—ฌ๊ธฐ์— ๋Œ€๊ธฐ์—ด ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” "ํ”Œ๋ ˆ์ด์–ด" ๋Œ€์‹  "๊ทธ๋ฃน" ํ˜•ํƒœ์˜ ์ถ”์ƒํ™”๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋‹จ์ผ ํ”Œ๋ ˆ์ด์–ด์˜ ๊ฒฝ์šฐ ๊ทธ๋ฃน์€ ๊ทธ๋ฃน์˜ ๋ชจ๋“  ํ”Œ๋ ˆ์ด์–ด๋กœ ๊ตฌ์„ฑ๋˜๊ณ  ๊ทธ๋ฃน์˜ ํ”Œ๋ ˆ์ด์–ด๋Š” ๊ฐ๊ฐ ๊ทธ๋ฃน์˜ ๋ชจ๋“  ํ”Œ๋ ˆ์ด์–ด๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

export default interface IQueue extends EventEmitter {
  inQueue: QueuePlayer[]
  put(uid: Party): boolean;
  remove(uid: Party): boolean;
  removeAll(ids: Party[]): void;

  mode: MatchmakingMode
  roomSize: number;
  clear(): void
}

๋‚˜๋Š” ์ƒํ™ฉ์„ ๊ตํ™˜ํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ๋ก€์— ์ ํ•ฉํ–ˆ์Šต๋‹ˆ๋‹ค. "10์ธ์šฉ ๊ฒŒ์ž„์ด ๋ฐœ๊ฒฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค"๋ผ๋Š” ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๊ฐœ์ธ ๋ฉ”์‹œ์ง€๋กœ ํ”Œ๋ ˆ์ด์–ด์—๊ฒŒ ํ•„์š”ํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๊ณ  ๊ธฐ๋ณธ์ ์ธ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ค€๋น„ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๋Š” ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๊ณ  ๋กœ๋น„๋ฅผ ์ค€๋น„ํ•ฉ๋‹ˆ๋‹ค. ์ถœ์‹œ ๋“ฑ์„ ์œ„ํ•ด.

IOC์˜ ๊ฒฝ์šฐ InversifyJS๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด ๋„์„œ๊ด€์—์„œ ์ผํ•˜๋ฉด์„œ ์ฆ๊ฑฐ์šด ๊ฒฝํ—˜์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋น ๋ฅด๊ณ  ์‰ฝ๊ฒŒ!

์šฐ๋ฆฌ ์„œ๋ฒ„์—๋Š” ์—ฌ๋Ÿฌ ๋Œ€๊ธฐ์—ด์ด ์žˆ์Šต๋‹ˆ๋‹ค. 1x1, ์ผ๋ฐ˜/๋“ฑ๊ธ‰ ๋ฐ ๋ช‡ ๊ฐ€์ง€ ์‚ฌ์šฉ์ž ์ •์˜ ๋ชจ๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์‚ฌ์šฉ์ž์™€ ๊ฒŒ์ž„ ๊ฒ€์ƒ‰ ์‚ฌ์ด์— ์‹ฑ๊ธ€ํ†ค RoomService๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

constructor(
    @inject(GameServers) private gameServers: GameServers,
    @inject(MatchStatsService) private stats: MatchStatsService,
    @inject(PartyService) private partyService: PartyService
  ) {
    super();
    this.initQueue(MatchmakingMode.RANKED);
    this.initQueue(MatchmakingMode.UNRANKED);
    this.initQueue(MatchmakingMode.SOLOMID);
    this.initQueue(MatchmakingMode.DIRETIDE);
    this.initQueue(MatchmakingMode.GREEVILING);
    this.partyService.addListener(
      "party-update",
      (event: PartyUpdatedEvent) => {
        this.queues.forEach((q) => {
          if (has(q.queue, (t) => t.is(event.party))) {
            // if queue has this party, we re-add party
            this.leaveQueue(event.qp, q.mode)
            this.enterQueue(event.qp, q.mode)
          }
        });
      }
    );

    this.partyService.addListener(
      "party-removed",
      (event: PartyUpdatedEvent) => {
        this.queues.forEach((q) => {
          if (has(q.queue, (t) => t.is(event.party))) {
            // if queue has this party, we re-add party
            q.remove(event.party)
          }
        });
      }
    );
  }

(ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋Œ€๋žต์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€์— ๋Œ€ํ•œ ์•„์ด๋””์–ด๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ๊ตญ์ˆ˜๋ฅผ ์ฝ”๋”ฉ)

์—ฌ๊ธฐ์„œ๋Š” ๊ตฌํ˜„๋œ ๊ฐ ๊ฒŒ์ž„ ๋ชจ๋“œ์— ๋Œ€ํ•œ ๋Œ€๊ธฐ์—ด์„ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ๋Œ€๊ธฐ์—ด์„ ์กฐ์ •ํ•˜๊ณ  ์ผ๋ถ€ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด "๊ทธ๋ฃน"์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ๋„ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.

์ž, ์ˆ˜๊ณ ํ•˜์…จ์Šต๋‹ˆ๋‹ค. ์ฃผ์ œ์™€ ๊ด€๋ จ ์—†๋Š” ์ฝ”๋“œ ์กฐ๊ฐ์„ ์‚ฝ์ž…ํ•˜๊ณ  ์ด์ œ ๋ฐ”๋กœ ๋งค์น˜๋ฉ”์ดํ‚น์œผ๋กœ ๋„˜์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค.

์‚ฌ๋ก€๋ฅผ ๊ณ ๋ คํ•ด ๋ด…์‹œ๋‹ค:

1) ์‚ฌ์šฉ์ž๊ฐ€ ํ”Œ๋ ˆ์ด๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค.

2) ๊ฒ€์ƒ‰์„ ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด ๊ทธ๋Š” Gateway=Discord๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋ฉ”์‹œ์ง€์— ๋Œ€ํ•œ ๋ฐ˜์‘์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” Dota 2014 ๋งค์น˜๋ฉ”์ดํ‚น์„ ์ž‘์„ฑ ์ค‘์ž…๋‹ˆ๋‹ค.

3) ์ด ๊ฒŒ์ดํŠธ์›จ์ด๋Š” RoomService๋กœ ์ด๋™ํ•˜์—ฌ "Discord์˜ ์‚ฌ์šฉ์ž๊ฐ€ ๋Œ€๊ธฐ์—ด์— ๋“ค์–ด๊ฐ€๊ณ  ์‹ถ์–ดํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“œ: ๋“ฑ๊ธ‰ ์—†์Œ ๊ฒŒ์ž„"์ด๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค.

4) RoomService๋Š” ๊ฒŒ์ดํŠธ์›จ์ด์˜ ์š”์ฒญ์„ ์ˆ˜๋ฝํ•˜๊ณ  ์‚ฌ์šฉ์ž(๋ณด๋‹ค ์ •ํ™•ํ•˜๊ฒŒ๋Š” ์‚ฌ์šฉ์ž ๊ทธ๋ฃน)๋ฅผ ์›ํ•˜๋Š” ๋Œ€๊ธฐ์—ด๋กœ ํ‘ธ์‹œํ•ฉ๋‹ˆ๋‹ค.

5) ๋Œ€๊ธฐ์—ด์€ ํ”Œ๋ ˆ์ดํ•  ์ˆ˜ ์žˆ๋Š” ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์ถฉ๋ถ„ํ•  ๋•Œ๋งˆ๋‹ค ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ์ด๋ฒคํŠธ๋ฅผ ๋‚ด๋ณด๋ƒ…๋‹ˆ๋‹ค.

private onRoomFound(players: Party[]) {
    this.emit("room-found", {
      players,
    });
  }

6) RoomService๋Š” ์ด ์ด๋ฒคํŠธ๋ฅผ ๊ฑฑ์ •์Šค๋Ÿฝ๊ฒŒ ๊ธฐ๋Œ€ํ•˜๋ฉฐ ๋ชจ๋“  ๋Œ€๊ธฐ์—ด์„ ์ฆ๊ฒ๊ฒŒ ๋“ฃ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ž…๋ ฅ์œผ๋กœ ํ”Œ๋ ˆ์ด์–ด ๋ชฉ๋ก์„ ๋ฐ›๊ณ , ๊ทธ๋“ค๋กœ๋ถ€ํ„ฐ ๊ฐ€์ƒ์˜ "๋ฐฉ"์„ ํ˜•์„ฑํ•˜๊ณ , ๋ฌผ๋ก  ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•ฉ๋‹ˆ๋‹ค.

queue.addListener("room-found", (event: RoomFoundEvent) => {
      console.log(
        `Room found mode: [${mode}]. Time to get free room for these guys`
      );
      const room = this.getFreeRoom(mode);
      room.fill(event.players);

      this.onRoomFormed(room);
    });

7) ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” "์ตœ๊ณ "๊ถŒํ•œ์ธ ํด๋ž˜์Šค์— ๋„๋‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ด‡. ์ผ๋ฐ˜์ ์œผ๋กœ ๊ทธ๋Š” ๊ฒŒ์ดํŠธ์›จ์ด(๋Ÿฌ์‹œ์•„์–ด์—์„œ๋Š” ์–ผ๋งˆ๋‚˜ ์žฌ๋ฏธ์žˆ์–ด ๋ณด์ด๋Š”์ง€ ์ดํ•ดํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค)์™€ ์ค‘๋งค์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋…ผ๋ฆฌ ์‚ฌ์ด์˜ ์—ฐ๊ฒฐ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ๋ด‡์€ ์ด๋ฒคํŠธ๋ฅผ ์šฐ์—ฐํžˆ ๋“ฃ๊ณ  DiscordGateway์— ๋ชจ๋“  ์‚ฌ์šฉ์ž์—๊ฒŒ ์ค€๋น„ ํ™•์ธ์„ ๋ณด๋‚ด๋„๋ก ๋ช…๋ นํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” Dota 2014 ๋งค์น˜๋ฉ”์ดํ‚น์„ ์ž‘์„ฑ ์ค‘์ž…๋‹ˆ๋‹ค.

8) ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ 3๋ถ„ ์ด๋‚ด์— ๊ฒŒ์ž„์„ ๊ฑฐ๋ถ€ํ•˜๊ฑฐ๋‚˜ ์ˆ˜๋ฝํ•˜์ง€ ์•Š์œผ๋ฉด ๋Œ€๊ธฐ์—ด๋กœ ๋Œ์•„๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ชจ๋“  ์‚ฌ๋žŒ์„ ๋Œ€๊ธฐ์—ด๋กœ ๋˜๋Œ๋ฆฌ๊ณ  ๋‹ค์‹œ 10๋ช…์ด ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ๋ชจ๋“  ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๊ฒŒ์ž„์„ ์ˆ˜๋ฝํ•˜๋ฉด ํฅ๋ฏธ๋กœ์šด ๋ถ€๋ถ„์ด ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.

์ „์šฉ ์„œ๋ฒ„ ๊ตฌ์„ฑ

์šฐ๋ฆฌ ๊ฒŒ์ž„์€ Windows Server 2012๊ฐ€ ์„ค์น˜๋œ VDS์—์„œ ํ˜ธ์ŠคํŒ…๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ช‡ ๊ฐ€์ง€ ๊ฒฐ๋ก ์„ ๋‚ด๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ๊ฑฐ๊ธฐ์— ๋„์ปค๊ฐ€ ์—†์–ด์„œ ๋งˆ์Œ์— ๋งž์•˜์Šต๋‹ˆ๋‹ค.
  2. ์ง‘์„ธ๋ฅผ ์ ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค

์ž‘์—…์€ Linux์˜ VPS์—์„œ VDS์˜ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ €๋Š” Flask๋กœ ๊ฐ„๋‹จํ•œ ์„œ๋ฒ„๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๋„ค, ์ €๋Š” Python์„ ์ข‹์•„ํ•˜์ง€ ์•Š์ง€๋งŒ ์–ด๋–ป๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‚˜์š”? ์ด ์„œ๋ฒ„๋ฅผ Python์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋” ๋น ๋ฅด๊ณ  ์‰ฝ์Šต๋‹ˆ๋‹ค.

์ด๋Š” 3๊ฐ€์ง€ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค:

  1. ๊ตฌ์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ฒ„ ์‹œ์ž‘ - ์ง€๋„, ๊ฒŒ์ž„์„ ์‹œ์ž‘ํ•  ํ”Œ๋ ˆ์ด์–ด ์ˆ˜ ๋ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ธํŠธ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์— ๋Œ€ํ•ด ๊ธ€์„ ์“ฐ์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ฐค์— ๋ˆˆ๋ฌผ๊ณผ ์ฐข์–ด์ง„ ๋จธ๋ฆฌ์นด๋ฝ์ด ์„ž์ธ ๋ช‡ ๋ฆฌํ„ฐ์˜ ์ปคํ”ผ์— ๋Œ€ํ•ด์„œ๋Š” ๋‹ค๋ฅธ ์ด์•ผ๊ธฐ์ž…๋‹ˆ๋‹ค.
  2. ์—ฐ๊ฒฐ์— ์‹คํŒจํ•  ๊ฒฝ์šฐ ์„œ๋ฒ„๋ฅผ ์ค‘์ง€/๋‹ค์‹œ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ˆ˜๋™์œผ๋กœ๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” ๋ชจ๋“  ๊ฒƒ์ด ๊ฐ„๋‹จํ•˜๋ฉฐ ์ฝ”๋“œ ์˜ˆ์ œ๋„ ์ ์ ˆํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. 100์ค„ ์Šคํฌ๋ฆฝํŠธ

๊ทธ๋ž˜์„œ 10๋ช…์ด ๋ชจ์—ฌ ๊ฒŒ์ž„์„ ์ˆ˜๋ฝํ–ˆ์„ ๋•Œ ์„œ๋ฒ„๊ฐ€ ์‹œ์ž‘๋˜์—ˆ๊ณ  ๋ชจ๋‘๊ฐ€ ํ”Œ๋ ˆ์ด์— ์—ด์ค‘ํ–ˆ๊ณ  ๊ฒŒ์ž„์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋งํฌ๊ฐ€ ๋น„๊ณต๊ฐœ ๋ฉ”์‹œ์ง€๋กœ ์ „์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” Dota 2014 ๋งค์น˜๋ฉ”์ดํ‚น์„ ์ž‘์„ฑ ์ค‘์ž…๋‹ˆ๋‹ค.

๋งํฌ๋ฅผ ํด๋ฆญํ•˜๋ฉด ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๊ฒŒ์ž„ ์„œ๋ฒ„์— ์—ฐ๊ฒฐ๋˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์•ฝ 25๋ถ„ ํ›„์— ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์žˆ๋Š” ๊ฐ€์ƒ "๋ฐฉ"์ด ์ง€์›Œ์ง‘๋‹ˆ๋‹ค.

๊ธฐ์‚ฌ๊ฐ€ ์–ด์ƒ‰ํ•œ ์  ๋ฏธ๋ฆฌ ์‚ฌ๊ณผ๋“œ๋ฆฌ๋ฉฐ, ์˜ค๋žซ๋™์•ˆ ์—ฌ๊ธฐ์— ๊ธ€์„ ์“ฐ์ง€ ์•Š์•˜์œผ๋ฉฐ, ์ค‘์š”ํ•œ ๋ถ€๋ถ„์„ ๊ฐ•์กฐํ•˜๊ธฐ์—๋Š” ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ ๊ตญ์ˆ˜.

์ฃผ์ œ์— ๋Œ€ํ•œ ๊ด€์‹ฌ์ด ๋ณด์ด๋ฉด ๋‘ ๋ฒˆ์งธ ๋ถ€๋ถ„์ด ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—๋Š” srcds(์†Œ์Šค ์ „์šฉ ์„œ๋ฒ„)์šฉ ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ์•„๋งˆ๋„ ๋“ฑ๊ธ‰ ์‹œ์Šคํ…œ ๋ฐ ๊ฒŒ์ž„ ํ†ต๊ณ„๊ฐ€ ์žˆ๋Š” ์‚ฌ์ดํŠธ์ธ ๋ฏธ๋‹ˆ ๋„ํƒ€๋ฒ„ํ”„๊ฐ€ ํฌํ•จ๋œ ๋‚ด ๊ณ ํ†ต์ด ํฌํ•จ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ผ๋ถ€ ๋งํฌ:

  1. ๋‹น์‚ฌ ์›น์‚ฌ์ดํŠธ(ํ†ต๊ณ„, ๋ฆฌ๋”๋ณด๋“œ, ์†Œ๊ทœ๋ชจ ๋žœ๋”ฉ ํŽ˜์ด์ง€ ๋ฐ ํด๋ผ์ด์–ธํŠธ ๋‹ค์šด๋กœ๋“œ)
  2. ๋””์Šค์ฝ”๋“œ ์„œ๋ฒ„

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€