予測線を予測する

MikuHatsune2012-09-17

ソードアートオンライン6巻p62より抜粋。
 
今、主人公のキリトと、ヒロインのシノンが、オンラインゲームでの銃火器戦での設定について語っている。
 
シノン「…基本的には、いまあんたが言ったとおり、参加者三十人による同一マップでの遭遇戦。開始位置はランダムだけど、どのプレイヤーとも最低千メートル離れているから、いきなり目の前に敵が立っているってことにはならないわ」
キリト「せ、千メートル?ってことは、マップは相当広いのか…?」
シノン「あんた、ほんとにメール読んだの?そんなの、一番最初の段落に書いてあるわよ。本戦のマップは直径十キロの円形。(略)」
キリト「じゅ、十キロ!?でかいな…」
キリト「…それ、ちゃんと遭遇できるのか?ヘタすると、大会終了まで誰とも出くわさない可能性も…」
シノン「銃で撃ち合うゲームだもの、それくらいの広さは必要なのよ。スナイパーライフルの射程は一キロ近くあるし、アサルトライフルだって五百メートルくらいまで狙えるわ。狭いマップに三十人も押し込めたら、開始直後からバリバリ撃ち合いになって、あっという間に半分以上死んじゃうわよ」
 
プレイヤー数N、プレイヤーの座標(x,\hspace{3}y)、プレイヤーの最低距離D_p、フィールドの最大半径D_Fとすると、
\sqrt{x_i^2 + y_i^2}\leq D_F
sqrt{(x_i-x_j)^2+(y_i-y_j)^2}\geq D_p \hspace{3}(1\leq i,\hspace{3}j \leq N,\hspace{3}i\not= j)
を満たす(x, \hspace{3}y)を一括して決めた…かったのだが、ちょっとアイデアが出て来なかった(Isotonic regressionを考えてみたが…)ので、地味に一人ひとり座標を決めていく方法でシミュレーションした。

player <- 30 #プレイヤーの人数
p_dis <- 1000 #初期位置での、プレイヤー間の最低距離
F_r <- 5000 #フィールドの半径の大きさ

#一人目を決める
#極座標でやる
r1 <- runif(1, min=0, max=F_r)
th1 <- runif(1, min=0, max=2)
x1 <- r1*cos(2*pi*th1)
y1 <- r1*sin(2*pi*th1)

#プレイヤーの座標を逐一決めていく
i <- 1
while(i < player){
	r2 <- runif(1, min=0, max=F_r)
	th2 <- runif(1, min=0, max=2)
	x2 <- r2*cos(2*pi*th2)
	y2 <- r2*sin(2*pi*th2)
	dsit_vec <- sqrt((x1-x2)^2 + (y1-y2)^2) #各プレイヤーとの距離を計算する
	if(all(dsit_vec > p_dis)){ #みんなとの距離があれば次に進む
		i <- i + 1
		x1 <- c(x1, x2)
		y1 <- c(y1, y2)
	}
}

plot(x1, y1, pch=16, xlim=c(-1, 1)*F_r, ylim=c(-1, 1)*F_r)
plot.circle(ox=0, oy=0, r=F_r, col=4)
for(i in 1:length(x1)){
	plot.circle(ox=x1[i], oy=y1[i], r=p_dis, lty=2) #プレイヤーを中心に、最低距離が保たれているか円
}


スナイパーライフルの射程が云々で、試合開始と同時にドンパチやって半分死ぬ、というのを、スナイパーライフルの所持数、スナイパーライフルの射程距離、プレイヤー間最低距離、フィールドの大きさなどパラメータにしてシミュレーションしてみたかったが保留。
 
円を描く関数を借りた。

# 実際には lines 関数を呼ぶので,... には lines 関数が許容する引数を書くことができる。
plot.circle <- function(ox, oy, r, start=0, end=360, ...)
{
        plot.ellipse(ox, oy, r, r, 0, start, end, ...)
}

# 中心を (ox, oy) とする,半径 r の円を描き,内部を塗りつぶす。
# 実際には polygon 関数を呼ぶので,... には polygon 関数が許容する引数を書くことができる。
plot.circlef <- function(ox, oy, r, ...)
{
        plot.ellipse(ox, oy, r, r, 0, 0, 360, func=polygon, ...)
}

# 中心を (ox, oy) とする,長径 ra,短径 rb の楕円を描く。
# phi は楕円の傾き(長径が水平線となす角度),start, end には,描き始めと描き終わりの位置を指定できる。
# 長径を基準として,角度(度単位)で指定できる(0, 360 とすると,完全な楕円を描くことになる。
# 90, 270 とすると,左半分の楕円を描くことを指示することになる)。
# 実際には lines 関数を呼ぶので,... には lines 関数が許容する引数を書くことができる。
plot.ellipse <- function(ox, oy, ra, rb, phi=0, start=0, end=360, length=100, func=lines, ...)
{
        theta <- c(seq(radian(start), radian(end), length=length), radian(end))
        if (phi == 0) {
                func(ra*cos(theta)+ox, rb*sin(theta)+oy, ...)
        }
        else {
                x <- ra*cos(theta)
                y <- rb*sin(theta)
                phi <- radian(phi)
                cosine <- cos(phi)
                sine <- sin(phi)
                func(cosine*x-sine*y+ox, sine*x+cosine*y+oy, ...)
        }
}