UWSCで安直なバトルゲーム

遺伝的アルゴリズム、ってあるじゃない?
あれ、やってみたいなー、と思いまして。
お題を何にしようか考えて、、、バトルゲームはどうだろう、と。
適当にパラメーターと行動パターンがあるもの、ということで、作成してみた。



ルール

攻撃力・防御力・回避力・体力がそれぞれ1以上で、合計値が1000になるように割り振る。


コマンド制で、「攻撃」「防御」「回避攻撃」を選ぶ。
攻撃同士は、相討ち。両方ダメージ。
攻撃と防御は、攻撃を受けるも、防御力重視のダメージで済む。
攻撃と回避攻撃は、回避攻撃側の攻撃のみ通る。
防御同士、回避攻撃同士は、何もなし。
防御と回避攻撃は、回避攻撃側が防御力によるカウンター攻撃を受ける。
攻撃・防御・回避攻撃は、みすくみの関係ですね。


回避力の高い方が先制。同じはランダム。
回避力が高いと確率で攻撃がスカる。


相手の体力を0にしたら勝ち。

スクリプト

battle.uws

OPTION EXPLICIT


IFB GET_UWSC_NAME = "battle.uws" THEN
	DIM i, gen[1]
	FOR i = 0 TO 1
		SELECT SLCTBOX(SLCT_NUM, 0, "P" + i + "を選択してください", "弱い", "強い", "遺伝子入力", "Manual")
		CASE 0
			SELECT SLCTBOX(SLCT_NUM, 0, "P" + i + "を選択してください", "剛腕", "固い", "早い", "タフ")
			CASE 0
				gen[i] = "PgrLoTuywUeBt6uYPu1GR6fuwLU7mumFbaFmGDNQ5jUC5QbE7VpcMt451E4iblGtcrYExByqCy8j"
			CASE 1
				gen[i] = "7l33KS4QINEBl7uG0Gx3YSpCz9Mw6zpdEWGthmNvBLa6yhvqLlMCaGPAs0BEBsYOzNVKXxm1aeLT"
			CASE 2
				gen[i] = "vcZp2eQkZb6zxVYz7VUZ00lFNadlx2rTMmMkk4GEPDH2JvSmr9owOyrOS4DOaixcdJBPY9TrhNo8"
			CASE 3
				gen[i] = "Plp0Cx7ywUeBt6uYPhP5uT6UvFatClVr5b200DggjCQZ9dIs79Hu2jDBnjTablGtcrYExByqCy8j"
			DEFAULT
				gen[i] = Battle.CreateGen()
			SELEND
		CASE 1
			SELECT SLCTBOX(SLCT_NUM, 0, "P" + i + "を選択してください", "剛腕", "固い", "早い", "タフ")
			CASE 0
				gen[i] = "6DKHh3AuMuWoC1fN3ZUU03uzDsrYgW63ZyTJhd4pAX071dducqBmWPqyIw6syGT4bTSQqU3Ba4h0"
			CASE 1
				gen[i] = "Fz4bW2oYRRslRTJ7uNZNUuTyFW4ZaSJi88RHrNVowpVkdAgJuhtRHehI7h2DWUEKdOR4YHXuPkK1"
			CASE 2
				gen[i] = "KrBzjRf85CMkstSHF3ygMGHdpvH0htxuaBsWhW6q6KlFQkeoi9BhWw5ugqUiMHXBlHPngjqaPatW"
			CASE 3
				gen[i] = "2cMvqnLufGTu0CJZjfl3b86A7GqsSj0DgUIz1sOwvI3qRzi5yvoTB5dRKkKrzkJU7N55OiOVJEd4"
			DEFAULT
				gen[i] = "CBLxg2IBdrMkstSHF3ygMGHdpvqsSj0DgUIz1sOw6KlFQkeoi9BhFkxzvIvOdXU2fttJi1E5bwVx"
			SELEND
		CASE 2
			gen[i] = INPUT("アルファベット大文字・小文字、数字を76文字まで入力してください", Battle.CreateGen())
		CASE 3
			gen[i] = "PLAYER"
		DEFAULT
			gen[i] = Battle.CreateGen()
		SELEND
	NEXT
	Battle.Do(gen)
ENDIF



MODULE Battle
	PUBLIC LogLevel = 5

	PUBLIC HASHTBL _adicn = HASH_CASECARE

	PROCEDURE Log(msg, level=4)
		IF level < LogLevel THEN PRINT msg
	FEND

	PROCEDURE Battle
		DIM i
		FOR i = 0 TO 9
			_adicn["" + i] = i
		NEXT
		FOR i = 0 TO 25
			_adicn[CHR(ASC("a") + i)] = i + 10
		NEXT
		FOR i = 0 TO 25
			_adicn[CHR(ASC("A") + i)] = i + 36
		NEXT
	FEND

	FUNCTION Do(gen[], n=1)
		// 遺伝子を展開
		DIM atk[1], def[1], eva[1], hp[1], hpo[1], ran[1][2], pat[1], i, j, t[3], r[1]
		FOR i = 0 TO 1
			IFB gen[i] = "PLAYER" THEN
				hpo[i] = 0
				WHILE hpo[i] = 0
					atk[i] = INT(VAL(INPUT("攻撃力を入力してください。<#CR><#CR>攻撃力+防御力+回避力+体力=1000になるようにしてください", 250)))
					def[i] = INT(VAL(INPUT("防御力を入力してください。", (1000-atk[i])/3)))
					eva[i] = INT(VAL(INPUT("回避力を入力してください。", (1000-atk[i]-def[i])/2)))
					hpo[i] = CalcHp(atk[i], def[i], eva[i])
					IF MSGBOX("攻撃力:" + atk[i] + "<#CR>防御力:" + def[i] + "<#CR>回避力:" + eva[i] + "<#CR>体力 :" + hpo[i] + "<#CR>でよろしいですか?", BTN_YES OR BTN_NO) = BTN_NO THEN hpo[i] = 0
				WEND
			ELSE
				IF LENGTH(gen[i] < 76) THEN gen[i] = gen[i] + FORMAT("0", 76 - LENGTH(gen[i]))
				AnalyzeGen(gen[i], atk[i], def[i], eva[i], hpo[i], ran[i][0], ran[i][1], ran[i][2], pat[i])
			ENDIF
			Log("P" + i + " : atk(" + atk[i] + ") def(" + def[i] + ") eva(" + eva[i] + ") hp(" + hpo[i] + ") " + gen[i], 3)
		NEXT

		DIM s[1], c = 0, w = 0
		WHILE w < n AND (c - w < n)
			// 最初の二手の実行
			FOR j = 0 TO 1
				hp[j] = hpo[j]
				r[j] = 0
				IFB gen[j] = "PLAYER" THEN
					s[j] = GetCmd(hp[j], hpo[j])
				ELSE
					s[j] = COPY(pat[j], 1, 1)
				ENDIF
			NEXT
			t[0] = ProcStep(atk, def, eva, hp, ran[0][r[0]], ran[1][r[1]], s[0], s[1], 1)
			IFB hp[0] > 0 AND hp[1] > 0 THEN
				FOR j = 0 TO 1
					IFB gen[j] = "PLAYER" THEN
						s[j] = GetCmd(hp[j], hpo[j])
					ELSE
						s[j] = COPY(pat[j], 2, 1)
					ENDIF
				NEXT
				t[1] = ProcStep(atk, def, eva, hp, ran[0][r[0]], ran[1][r[1]], s[0], s[1], 2)
			ENDIF
			// 残りの実行
			i = 3
			WHILE hp[0] > 0 AND hp[1] > 0
				IFB GETKEYSTATE(VK_F1) THEN
					SELECT SLCTBOX(SLCT_NUM, 0, "強制介入", "ログレベル変更", "P0勝利", "P1勝利")
					CASE 0
						LogLevel = VAL(INPUT("レベルは?", LogLevel))
					CASE 1
						hp[1] = 0
						BREAK
					CASE 2
						hp[0] = 0
						BREAK
					SELEND
				ENDIF
				t[2] = t[0] * 9 + t[1]
				t[3] = ((t[0] MOD 3) * 3 + INT(t[0] / 3)) * 9 + ((t[1] MOD 3) * 3 + INT(t[1] / 3))
				Log(" 2手前:" + t[0] + " 1手前:" + t[1], 5)
				t[0] = t[1]
				FOR j = 0 TO 1
					IFB hp[j] < hpo[j] / 3 THEN
						r[j] = 2
					ELSEIF hp[j] < hpo[j] * 2 / 3 THEN
						r[j] = 1
					ENDIF
					IFB gen[j] = "PLAYER" THEN
						s[j] = GetCmd(hp[j], hpo[j])
					ELSE
						s[j] = COPY(pat[j], 3 + t[2+j] + r[j] * 81, 1)
					Log(" 組み合わせ:" + t[2+j] + " hp:" + hp[j] + "/" + hpo[j] + "=" + r[j] + " 手:" + s[j], 5)
					ENDIF
				NEXT
				t[1] = ProcStep(atk, def, eva, hp, ran[0][r[0]], ran[1][r[1]], s[0], s[1], i)
				i = i + 1
			WEND
			c = c + 1
			IFB hp[0] > 0 THEN
				w = w + 1
				Log("P0の1本! " + w)
			ELSE
				Log("P1の1本! " + (c - w))
			ENDIF
		WEND
		IFB w < n THEN
			Log("P1の勝利!", 3)
			RESULT = 1
		ELSE
			Log("P0の勝利!", 3)
			RESULT = 0
		ENDIF
	FEND

	FUNCTION GetCmd(hp, hpo)
		RESULT = "" + SLCTBOX(SLCT_NUM, 0, "コマンド HP(" + hp + "/" + hpo + ")", "攻撃", "防御", "回避攻撃")
	FEND

	// 処理
	FUNCTION ProcStep(atk[], def[], eva[], var hp[], ran0, ran1, c0, c1, turn)
		// 乱数による手の差し替え判定
		DIM i, c, t[] = 0, 0
		c = c0 + c1
		IF ran0 > RANDOM(1000) THEN c0 = "" + RANDOM(3)
		IF ran1 > RANDOM(1000) THEN c1 = "" + RANDOM(3)
		IF c <> c0 + c1 THEN Log(" P0 P1 " + c + " -> " + c0 + c1, 5)
		Log("ターン " + turn)
		SELECT c0 + c1
		CASE "00"
			t[0] = INT(atk[1] * (1 - def[0] / 1000))
			IF t[0] < 1 THEN t[0] = 1
			t[1] = INT(atk[0] * (1 - def[1] / 1000))
			IF t[1] < 1 THEN t[1] = 1
			Log(" 相討ち!")
			RESULT = 0
		CASE "01"
			t[1] = INT((atk[0] - def[1]) / 3)
			IF t[1] < 1 THEN t[1] = 1
			Log(" P0がガードの上から攻撃")
			RESULT = 1
		CASE "02"
			t[0] = INT(atk[1] * (1 - def[0] / 1000))
			IF t[0] < 1 THEN t[0] = 1
			Log(" P1が回避攻撃")
			RESULT = 2
		CASE "10"
			t[0] = INT((atk[0] - def[1]) / 3)
			IF t[0] < 1 THEN t[1] = 1
			Log(" P1がガードの上から攻撃")
			RESULT = 3
		CASE "11"
			Log(" 睨み合い")
			RESULT = 4
		CASE "12"
			t[1] = INT((def[0] - def[1]) / 3)
			IF t[1] < 1 THEN t[1] = 1
			Log(" P0がカウンター")
			RESULT = 5
		CASE "20"
			t[1] = INT(atk[0] * (1 - def[1] / 1000))
			IF t[1] < 1 THEN t[1] = 1
			Log(" P0が回避攻撃")
			RESULT = 6
		CASE "21"
			t[0] = INT((def[1] - def[0]) / 3)
			IF t[0] < 1 THEN t[0] = 1
			Log(" P1がカウンター")
			RESULT = 7
		CASE "22"
			Log(" 双方回避")
			RESULT = 8
		SELEND
		DIM f[] = 0, 0
		IFB eva[0] = eva[1] THEN
			f[0] = RANDOM(2)
		ELSE
			f[0] = (eva[0] < eva[1]) * 1
		ENDIF
		f[1] = 1 - f[0]
		FOR i = 0 TO 1
			IFB t[f[1]] > 0 AND hp[f[0]] > 0 THEN
				// 当たり判定
				IFB eva[f[1]] - eva[f[0]] < RANDOM(1000) THEN
					Log("  P" + f[1] + "は " + t[f[1]] + "のダメージ")
					hp[f[1]] = hp[f[1]] - t[f[1]]
				ELSE
					Log("  P" + f[0] + "はミスした")
				ENDIF
			ENDIF
			f[0] = f[1]
			f[1] = 1 - f[1]
		NEXT
	FEND

	// 遺伝子展開
	PROCEDURE AnalyzeGen(gen, var atk, var def, var eva, var hp, var ran0, var ran1, var ran2, var pat)
		// 最初の6*23パラ*2に。3進数243桁は、前のパラから2桁・3桁と、62進数8*8
		DIM n = Conv(COPY(gen, 1, 6)), i
		atk = n MOD 1000
		n = (n - atk) / 1000
		def = n MOD 1000
		n = (n - def) / 1000
		eva = n MOD 1000
		n = (n - eva) / 1000
		pat = ""
		FOR i = 1 TO 2
			pat = pat + (n MOD 3)
			n = INT(n / 3)
		NEXT
		Log("First: " + COPY(gen, 1, 6) + " " + Conv(COPY(gen, 1, 6)) + " atk:" + atk + " def:" + def + " eva:" + eva + " pat:" + pat, 5)
		n = Conv(COPY(gen, 7, 6))
		ran0 = n MOD 1000
		n = (n - ran0) / 1000
		ran1 = n MOD 1000
		n = (n - ran1) / 1000
		ran2 = n MOD 1000
		n = (n - ran2) / 1000
		FOR i = 1 TO 3
			pat = pat + (n MOD 3)
			n = INT(n / 3)
		NEXT
		Log("Second: " + COPY(gen, 7, 6) + " " + Conv(COPY(gen, 7, 6)) + " ran0:" + ran0 + " ran1:" + ran1 + " ran2:" + ran2 + " pat:" + pat, 5)
		FOR n = 0 TO 7
			pat = pat + Conv(COPY(gen, 13 + n * 8, 8), 3)
		NEXT
		hp = CalcHp(atk, def, eva)
		Log("pat:" + pat, 5)
	FEND
	FUNCTION CalcHp(var atk, var def, var eva)
		IF atk < 1 THEN atk = 1
		IF def < 1 THEN def = 1
		IF eva < 1 THEN eva = 1
		IFB atk + def + eva > 999 THEN
			IFB atk > def THEN
				IFB atk > eva THEN
					IFB eva + def > 998 THEN
						eva = 1
						def = 998 - atk
					ELSE
						atk = 999 - eva - def
					ENDIF
				ELSE
					IFB atk + def > 998 THEN
						eva = 1
						def = 998 - atk
					ELSE
						eva = 999 - atk - def
					ENDIF
				ENDIF
			ELSE
				IFB def > eva THEN
					IFB eva + atk > 998 THEN
						eva = 1
						def = 998 - atk
					ELSE
						def = 999 - eva - atk
					ENDIF
				ELSE
					IFB def + atk > 998 THEN
						eva = 1
						def = 998 - atk
					ELSE
						eva = 999 - def - atk
					ENDIF
				ENDIF
			ENDIF
		ENDIF
		RESULT = 1000 - atk - def - eva
	FEND

	FUNCTION Conv(numstr, adicn=0)
		RESULT = 0
		DIM i, b = LENGTH(_adicn)
		FOR i = 1 TO LENGTH(numstr)
			RESULT = RESULT * b + _adicn[COPY(numstr, i, 1)]
		NEXT
		IFB adicn > 0 THEN
			i = RESULT
			RESULT = ""
			DIM c
			FOR c = 1 TO INT(LOGN(adicn, POWER(b, LENGTH(numstr))))
				RESULT = RESULT + (i MOD adicn)
				i = INT(i / adicn)
			NEXT
			IF LENGTH(RESULT) = 0 THEN RESULT = "0"
		ENDIF
	FEND

	// 遺伝子生成
	FUNCTION CreateGen()
		DIM i, b = LENGTH(_adicn)
		RESULT = ""
		FOR i = 1 TO 76
			RESULT = RESULT + _adicn[RANDOM(b), HASH_KEY]
		NEXT
	FEND

ENDMODULE


弱いグループは適当にやって勝てますが、強いグループは、、、強い、、、かな?
遺伝子には、上記のことが含まれているのです。