///|
/// four_peer:四个人同时对局。
///
/// 每个玩家一个 `Session`,与其他三人各有一条独立链路(4 人全互连 = 12 条),
/// 包照样走真实字节协议。示例会把「2 人局」和「4 人局」按同一套链路参数各跑
/// 一遍,对比重放帧数——多一个人在局里,代价不是线性的。
///
///   moon run examples/four_peer
const PLAYERS : Int32 = 4

///|
const TOTAL : Int32 = 480

///|
const RESEND : Int32 = 8

///|
const DELAY : Int32 = 3

///|
const JITTER : Int32 = 1

///|
/// 一局跑完的结果。
pub(all) struct RunStats {
  /// 每个玩家的确认帧。
  confirmed : Array[Int32]
  /// 所有玩家回滚次数之和。
  rollbacks : Int64
  /// 所有玩家重放帧数之和。
  replayed : Int64
  /// 所有玩家单次最大回滚深度里的最大值。
  max_depth : Int32
  /// 校验和帧比对失败次数。
  mismatches : Int32
  /// 首个分叉帧;-1 表示全程一致。
  first_divergence : Int32
}

///|
/// 把某个玩家最近 `RESEND` 帧的输入、当前确认帧和该帧哈希打成一个包。
fn outbound(
  s : @session.Session[@scene.Scene],
  me : Int32,
  back : Int32,
  f : Int32,
) -> Bytes {
  let msgs : Array[@protocol.Message] = []
  let mut m = back
  while m <= f {
    match s.player_input(me, m) {
      Some(bits) =>
        msgs.push(
          @protocol.Message::Input(
            @protocol.InputFrame::new_from(me, m, [bits]),
          ),
        )
      None => ()
    }
    m = m + 1
  }
  let c = s.confirmed()
  msgs.push(@protocol.Message::Confirm(@protocol.ConfirmFrame::new(c)))
  match s.hash_at(c) {
    Some(h) =>
      msgs.push(@protocol.Message::Checksum(@protocol.ChecksumFrame::new(c, h)))
    None => ()
  }
  @protocol.encode_batch(msgs)
}

///|
/// 找首个分叉帧:从 1 扫到大家的公共确认帧,逐帧比对四个会话的历史哈希。
fn scan_divergence(sessions : Array[@session.Session[@scene.Scene]]) -> Int32 {
  let mut c = sessions[0].confirmed()
  let mut i = 1
  while i < sessions.length() {
    if sessions[i].confirmed() < c {
      c = sessions[i].confirmed()
    }
    i = i + 1
  }
  let mut f = 1
  while f <= c {
    let base = sessions[0].hash_at(f)
    let mut j = 1
    while j < sessions.length() {
      if sessions[j].hash_at(f) != base {
        return f
      }
      j = j + 1
    }
    f = f + 1
  }
  -1
}

///|
/// 跑一局 `players` 人对局,`verbose` 为真时每 60 帧打一行进度。
pub fn run(players : Int32, total : Int32, verbose : Bool) -> RunStats {
  let sessions : Array[@session.Session[@scene.Scene]] = []
  let mut i = 0
  while i < players {
    sessions.push(
      @session.Session::with_players(@scene.Scene::new(), players, i, 128),
    )
    i = i + 1
  }
  // nets[from][to] 是一条单向链路,from == to 的那格不会被使用。
  let nets : Array[Array[@sim_net.NetSim]] = []
  let mut from = 0
  while from < players {
    let row : Array[@sim_net.NetSim] = []
    let mut to = 0
    while to < players {
      row.push(
        @sim_net.NetSim::new(
          @sim_net.Link::new(
            DELAY,
            JITTER,
            0,
            0,
            ((from * 977 + to).to_uint64() + 1UL) * 7919UL,
          ),
        ),
      )
      to = to + 1
    }
    nets.push(row)
    from = from + 1
  }
  let mut mismatches = 0
  let mut f = 0
  while f < total {
    f = f + 1
    let mut p = 0
    while p < players {
      ignore(sessions[p].advance(@scene.scripted_input(f, p)))
      p = p + 1
    }
    let mut back = f - RESEND + 1
    if back < 1 {
      back = 1
    }
    // 每个玩家算一次包,发给其他所有人。
    let mut sender = 0
    while sender < players {
      let payload = outbound(sessions[sender], sender, back, f)
      let mut target = 0
      while target < players {
        if target != sender {
          nets[sender][target].send(f, payload)
        }
        target = target + 1
      }
      sender = sender + 1
    }
    // 每个玩家收其他人的包。
    let mut target = 0
    while target < players {
      let mut src = 0
      while src < players {
        if src != target {
          for payload in nets[src][target].recv(f) {
            mismatches = mismatches + @scene.apply(payload, sessions[target])
          }
        }
        src = src + 1
      }
      target = target + 1
    }
    if verbose && f % 60 == 0 {
      let mut line = "\{pad(f.to_string(), 6)}"
      let mut k = 0
      while k < players {
        line = line + pad(sessions[k].confirmed().to_string(), 8)
        k = k + 1
      }
      let mut rb = 0
      let mut k2 = 0
      while k2 < players {
        rb = rb + sessions[k2].rollbacks()
        k2 = k2 + 1
      }
      println("\{line}\{pad(rb.to_string(), 10)}")
    }
  }
  // 收尾:最后一轮的包还在路上,多收 64 帧让确认追上。
  let mut t = total
  while t < total + 64 {
    t = t + 1
    let mut target = 0
    while target < players {
      let mut src = 0
      while src < players {
        if src != target {
          for payload in nets[src][target].recv(t) {
            mismatches = mismatches + @scene.apply(payload, sessions[target])
          }
        }
        src = src + 1
      }
      target = target + 1
    }
  }
  let confirmed : Array[Int32] = []
  let mut rollbacks = 0L
  let mut replayed = 0L
  let mut max_depth = 0
  let mut k = 0
  while k < players {
    confirmed.push(sessions[k].confirmed())
    rollbacks = rollbacks + sessions[k].rollbacks().to_int64()
    replayed = replayed + sessions[k].replayed()
    if sessions[k].max_rollback_depth() > max_depth {
      max_depth = sessions[k].max_rollback_depth()
    }
    k = k + 1
  }
  {
    confirmed,
    rollbacks,
    replayed,
    max_depth,
    mismatches,
    first_divergence: scan_divergence(sessions),
  }
}

///|
fn main {
  println(
    "four_peer:\{PLAYERS} 个 Session 全互联 + 真实字节协议 + \{DELAY} 帧延迟 + \{JITTER} 帧抖动",
  )
  println("")
  println("frame  confirm(p0..p3)              rollbacks")
  let four = run(PLAYERS, TOTAL, true)
  let two = run(2, TOTAL, false)
  println("")
  println("终态(4 人局):")
  println(
    "  一致 = \{four.first_divergence < 0},首个分叉帧 = \{if four.first_divergence < 0 { "无" } else { four.first_divergence.to_string() }},校验失配 = \{four.mismatches}",
  )
  println(
    "  确认帧 = \{four.confirmed[0]}/\{four.confirmed[1]}/\{four.confirmed[2]}/\{four.confirmed[3]},回滚 = \{four.rollbacks},重放帧数 = \{four.replayed},最大深度 = \{four.max_depth}",
  )
  println("")
  println("同样 480 帧、同样链路参数下的规模对比:")
  println("  玩家数  链路数  回滚次数  重放帧数  最大深度")
  println(
    "  \{pad("2", 8)}\{pad("2", 8)}\{pad(two.rollbacks.to_string(), 10)}\{pad(two.replayed.to_string(), 10)}\{two.max_depth}",
  )
  println(
    "  \{pad(PLAYERS.to_string(), 8)}\{pad("12", 8)}\{pad(four.rollbacks.to_string(), 10)}\{pad(four.replayed.to_string(), 10)}\{four.max_depth}",
  )
  println("")
  println(
    "结论:每人多一条链路、每帧多一路输入,重放成本涨得比人数快——",
  )
  println(
    "      回滚会话的窗口与冗余发送,都是按「最慢的那个玩家」付钱的。",
  )
}

///|
/// 右侧补空格,让表格在等宽字体里对齐。
fn pad(s : String, width : Int32) -> String {
  let mut out = s
  while out.length() < width {
    out = out + " "
  }
  out
}