-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
127 changed files
with
543,693 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,14 @@ | ||
# adaptive_game_control | ||
ChEn 436 Process Control final project: adaptive game difficulty | ||
|
||
The basic file structure here is as follows: | ||
|
||
`sim.py` implements the basic gameplay simulation, as well as classification system and PID control system. | ||
|
||
`main.py` will run a simulation with `skill` and `style` parameters set inside the file, and generate and save some plots about that simulation. (It calls `plt.show`, so depending on where you run the code, it will probably also show you the plots.) | ||
|
||
`params.py` is used to run a large number of simulations, for the purpose of gathering statistics; it also uses those simulations to determine values for process gain $K_p$ and time constant $\tau_p$. (The generated values are stored in `params.txt` here, so you do not need to run this unless you want to recalculate $K_p$ and $\tau_p$. The simulation results are all stored in `sim_results` folder. | ||
|
||
`trends_gen.py` generates the reference file which our classification system uses. (Again, we have provided this output, `trendslist.data`, which is a pickled Python list, so you do not need to run this file.) It uses saved output files from `params.py`, so if you choose to run this, you should run `params.py` first. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import numpy as np | ||
from scipy.integrate import odeint | ||
from scipy.optimize import curve_fit | ||
import matplotlib.pyplot as plt | ||
import numpy.random as rnd | ||
import pickle | ||
# %matplotlib inline | ||
|
||
# from google.colab import drive | ||
# drive.mount('/content/gdrive') | ||
|
||
|
||
# Other files for this project | ||
import sim | ||
# import params | ||
|
||
# proj_dir = "/content/gdrive/My Drive/ProcControl Project/" | ||
proj_dir = "./" | ||
|
||
style = "reckless" | ||
skill = 5 | ||
|
||
Npts = 6000 # seconds | ||
time = np.linspace(0,Npts-1,Npts) | ||
dt = time[1]-time[0] | ||
|
||
#Initialize Character Stats array | ||
# At_char = np.ones(Npts)*10 #Character Attack | ||
HP0 = 100 | ||
At_char = 1 | ||
skill = 5 | ||
style = "conservative" | ||
# style = "reckless" | ||
# style = "moderate" | ||
HP_char = np.zeros(Npts) #Character Health | ||
HP_char[0] = HP0 | ||
|
||
|
||
|
||
#Enemy Initial Stats | ||
Enemy0 = [10,1] #Enemy0 = [Health, Attack] # | ||
Enemy = list(Enemy0) | ||
At_enem = np.ones(Npts) | ||
HP_enem = np.ones(Npts) * Enemy0[0] | ||
|
||
|
||
#If we use average damage per time as our set point we will use the following | ||
SP_DPS = HP0/100 #the PID will attempt to have the player loose this much health per time (damage/second) | ||
SP_DPS = HP0/150 | ||
|
||
######### | ||
|
||
tauD = 0 | ||
|
||
dt = 1 | ||
|
||
# Run a simulation | ||
HP_char, At_enem, HP_enem, etod, interr_arr = sim.sim_gameplay(style, skill, control=True) | ||
filterwidth = 20 | ||
# DPS = -(HP_char[filterwidth:] - HP_char[:-filterwidth])/filterwidth | ||
DPS = -(HP_char[1:] - HP_char[0]) / time[1:] | ||
DPS[0] = 0 | ||
# print(At_enem[:etod]) | ||
err = SP_DPS - DPS | ||
SAE = np.sum(np.abs(err[:etod])) | ||
# print(err[:etod]) | ||
print("SAE", SAE) | ||
|
||
# Plot results up until time of death | ||
|
||
plt.plot(time[0:etod],HP_char[0:etod],'k',label='HP') | ||
plt.title('Character Health',fontsize=24) | ||
plt.xlabel('Time (sec)',fontsize=18) | ||
plt.ylabel('Health',fontsize=18) | ||
plt.axvline(150, color="gray", ls="--", label="target time") | ||
plt.text(etod*.8, 80, f"skill={skill}") | ||
plt.text(etod*.7, 60, f"style={style}") | ||
plt.legend(fontsize=14) | ||
plt.ylim(0, 100) | ||
plt.xlim(0, etod) | ||
plt.savefig(proj_dir + f"health_skill{skill:d}_style{style[0]}.png"); | ||
plt.show() | ||
|
||
plt.title("Damage per Second",fontsize=24) | ||
plt.plot(time[0:etod], DPS[0:etod]) | ||
# plt.plot(time[0:etod], err[0:etod]) | ||
plt.text(0, 1.2, f"SAE={SAE}") | ||
# plt.plot(time[0:etod], interr_arr[0:etod]) | ||
plt.plot([0,etod],[SP_DPS,SP_DPS]) | ||
plt.xlabel('Time (sec)',fontsize=18) | ||
plt.ylabel('Damage per second',fontsize=18) | ||
plt.ylim(0, 1.5) | ||
plt.axvline(150, color="gray", ls="--", label="") | ||
plt.savefig(proj_dir + f"dps_skill{skill:d}_style{style[0]}.png"); | ||
plt.show() | ||
|
||
plt.plot(time[0:etod],At_enem[0:etod],'k',label='Enemy attack') | ||
plt.title('Enemy Attack',fontsize=24) | ||
plt.xlabel('Time (sec)',fontsize=18) | ||
plt.ylabel('Attack',fontsize=18) | ||
plt.savefig(proj_dir + f"eatk_skill{skill:d}_style{style[0]}.png"); | ||
plt.show(); | ||
|
||
plt.plot(time[0:etod],HP_enem[0:etod],'k',label='HP') | ||
plt.title('Enemy Health',fontsize=24) | ||
plt.xlabel('Time (sec)',fontsize=18) | ||
plt.ylabel('Health',fontsize=18) | ||
plt.show(); | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
|
||
import numpy as np | ||
from scipy.integrate import odeint | ||
from scipy.optimize import curve_fit | ||
import matplotlib.pyplot as plt | ||
import numpy.random as rnd | ||
import pickle | ||
|
||
import sim | ||
|
||
HP0 = 100 | ||
Npts = 6000 | ||
time = np.arange(Npts+1) | ||
Enemy0 = [10, 1] | ||
At_char = 1 | ||
proj_dir = "./" | ||
|
||
|
||
# Run a set of simulations, info for each, save to file | ||
N_sim = 100 | ||
sk_list = range(1, 11) | ||
st_list = ["conservative", "moderate", "reckless"] | ||
for skill in sk_list: | ||
for style in st_list: | ||
# etod_list = [] | ||
# for n in range(N_sim): | ||
# HP_char, At_enem, HP_enem, etod = sim.sim_gameplay(style, skill) | ||
# plt.plot(time[0:etod],HP_char[0:etod],'k',label='HP') | ||
# etod_list.append(etod) | ||
# etod_arr = np.array(etod_list) | ||
HP_arr_arr = np.array([sim.sim_gameplay(style, skill, adj=True)[0] for n in range(N_sim)]) | ||
HP_arr = np.mean(HP_arr_arr, axis=0) | ||
enem_arr_arr = np.array([sim.sim_gameplay(style, skill, adj=True)[2] for n in range(N_sim)]) | ||
etod_arr = np.array([sim.sim_gameplay(style, skill)[3] for n in range(N_sim)]) | ||
|
||
np.savetxt(proj_dir+"sim_results/"+f"sim_results_skill{skill}_style{style[0]}_mod.txt", HP_arr.T, fmt="%d") | ||
np.savetxt(proj_dir+"sim_results/"+f"sim_results_skill{skill}_style{style[0]}.txt", etod_arr.T, fmt="%d") | ||
np.savetxt(proj_dir+"sim_results/"+f"HP_results_skill{skill}_style{style[0]}.txt", HP_arr_arr.T, fmt="%d") | ||
np.savetxt(proj_dir+"sim_results/"+f"enem_results_skill{skill}_style{style[0]}.txt", enem_arr_arr.T, fmt="%d") | ||
|
||
# Generate a summary plot across skill level, style types | ||
col_list = ["blue", "green", "red"] | ||
lab_list = ["conservative", "moderate", "reckless"] | ||
full_list = [] | ||
for i, st in enumerate(st_list): | ||
sub_list = [] | ||
for sk in sk_list: | ||
dat_aa = np.loadtxt(proj_dir+"sim_results/"+f"sim_results_skill{sk}_style{st[0]}.txt") | ||
sub_list.append(dat_aa) | ||
full_list.append(sub_list) | ||
full_arr = np.array(full_list) | ||
print(full_arr.shape) | ||
ave_DPS = [] | ||
for i, st in enumerate(full_arr): | ||
# print(f) | ||
plt.plot(range(1, 11), st, "x", color=col_list[i]) | ||
for sk in st.T: | ||
|
||
# plt.plot(range(1,11), sk, "x", color=col_list[i],) | ||
pass | ||
plt.plot(range(1, 11), np.mean(st, axis=1), "-", color=col_list[i], label=lab_list[i]+" playstyle") | ||
ave_DPS.append(100/np.mean(st, axis=1)) | ||
|
||
plt.legend() | ||
plt.ylabel("Time steps before death") | ||
plt.xlabel("Skill level") | ||
plt.title("Comparison of game time vs. skill level and play style") | ||
|
||
print(ave_DPS) | ||
|
||
# Generate a summary plot across skill level, style types | ||
col_list = ["blue", "green", "red"] | ||
lab_list = ["conservative", "moderate", "reckless"] | ||
full_list = [] | ||
for i, st in enumerate(st_list): | ||
sub_list = [] | ||
for sk in sk_list: | ||
dat_aa = np.loadtxt(proj_dir+"sim_results/"+f"HP_results_skill{sk}_style{st[0]}.txt") | ||
sub_list.append(dat_aa) | ||
full_list.append(sub_list) | ||
full_arr_step = np.array(full_list) | ||
print(full_arr_step.shape) | ||
tau_arr = [] | ||
gain_arr = [] | ||
for i, st in enumerate(full_arr_step): | ||
# print(f) | ||
# plt.plot(st, "x", color=col_list[i]) | ||
# print(st.shape) | ||
for j, sk in enumerate(st): | ||
# print(len(sk)) | ||
tau_list = [] | ||
gain_list = [] | ||
for sim in sk.T: | ||
|
||
etod = np.where(sim==0)[0][0] | ||
step = np.where(sim<=50)[0][0] | ||
qd = np.where(sim<=25)[0][0] | ||
tau_list.append(qd-step) | ||
DPS = (HP0 - sim[step:etod])/time[step:etod] | ||
alt_etod = full_arr[i, j] | ||
alt_DPS = 100/alt_etod | ||
gain = DPS[-1]-alt_DPS | ||
gain_list.append(gain) | ||
plt.plot(time[:etod-step], DPS[:etod]) | ||
# plt.plot(range(1,11), sim, "x", color=col_list[i],) | ||
pass | ||
tau_arr.append(np.mean(tau_list)) | ||
gain_arr.append(np.mean(gain_list)) | ||
plt.title(f"skill {j+1}, style {lab_list[i][:3]}") | ||
plt.show() | ||
# plt.plot(range(1, 11), np.mean(st, axis=1), "-", color=col_list[i], label=lab_list[i]+" playstyle") | ||
tau_arr = np.array(tau_arr) | ||
gain_arr = np.array(gain_arr) | ||
print("tau", tau_arr) | ||
print("gain", gain_arr) | ||
dat = np.column_stack([tau_arr, gain_arr]) | ||
np.savetxt("params.txt", dat) | ||
# plt.legend() | ||
# plt.ylabel("DPS") | ||
# plt.xlabel("Time after step change") | ||
# plt.xlim(0, 100) | ||
# plt.ylim() | ||
# plt.title("Comparison of game time vs. skill level and play style") | ||
|
||
gain1 = gain_arr[0:10] #conservative | ||
gain2 = gain_arr[10:20] #moderate | ||
gain3 = gain_arr[20:30] #reckless | ||
tau1 = tau_arr[0:10] #conservative | ||
tau2 = tau_arr[10:20] #moderate | ||
tau3 = tau_arr[20:30] #reckless | ||
plt.plot(sk_list, gain1, label=lab_list[0]) | ||
plt.plot(sk_list, gain2, label=lab_list[1]) | ||
plt.plot(sk_list, gain3, label=lab_list[2]) | ||
plt.legend() | ||
plt.ylabel("Gain $K_p$") | ||
plt.xlabel("Skill") | ||
|
||
def line(x, m, b): | ||
return m*x+b | ||
Kp1_fit, covar = curve_fit(line, sk_list, gain1) | ||
Kp2_fit, covar = curve_fit(line, sk_list, gain2) | ||
Kp3_fit, covar = curve_fit(line, sk_list, gain3) | ||
# plt.plot(sk_list, line(sk_list, *Kp1_fit)) | ||
# plt.plot(sk_list, line(sk_list, *Kp2_fit)) | ||
# plt.plot(sk_list, line(sk_list, *Kp3_fit)) | ||
|
||
plt.show() | ||
print(Kp1_fit) | ||
print(Kp2_fit) | ||
print(Kp3_fit) | ||
|
||
def exp_c(x, c1, c2, offset): | ||
return c1*np.exp(x*c2) + offset | ||
tau1_fit, covar = curve_fit(exp_c, sk_list, tau1) | ||
tau2_fit, covar = curve_fit(exp_c, sk_list, tau2) | ||
tau3_fit, covar = curve_fit(exp_c, sk_list, tau3) | ||
|
||
print(tau1_fit) | ||
print(tau2_fit) | ||
print(tau3_fit) | ||
|
||
|
||
|
||
plt.plot(sk_list, tau1, label=lab_list[0]) | ||
plt.plot(sk_list, tau2, label=lab_list[1]) | ||
plt.plot(sk_list, tau3, label=lab_list[2]) | ||
# plt.plot(sk_list, exp_c(sk_list, *tau1_fit)) | ||
# plt.plot(sk_list, exp_c(sk_list, *tau2_fit)) | ||
# plt.plot(sk_list, exp_c(sk_list, *tau3_fit)) | ||
|
||
plt.legend() | ||
plt.ylabel("Time constant $\\tau_p$") | ||
plt.xlabel("Skill") | ||
plt.show() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
6.185000000000000142e+01 9.110496310522270269e-02 | ||
6.135000000000000142e+01 1.147940699589847235e-01 | ||
5.800000000000000000e+01 1.198197093070172414e-01 | ||
6.820000000000000284e+01 1.526013081822418516e-01 | ||
6.665000000000000568e+01 1.582350139619470297e-01 | ||
7.190000000000000568e+01 1.700341781992531498e-01 | ||
7.979999999999999716e+01 1.788746883719655456e-01 | ||
7.879999999999999716e+01 1.855329733205805520e-01 | ||
1.056500000000000057e+02 1.902041469940962126e-01 | ||
1.228499999999999943e+02 1.682673875464476554e-01 | ||
3.400000000000000000e+01 5.366252242736072042e-02 | ||
3.454999999999999716e+01 8.786304769087074917e-02 | ||
4.395000000000000284e+01 7.382434740133875239e-02 | ||
4.589999999999999858e+01 8.687011060051549882e-02 | ||
4.304999999999999716e+01 1.898921342488902875e-01 | ||
4.400000000000000000e+01 2.042359441643654405e-01 | ||
4.900000000000000000e+01 2.136238917251379599e-01 | ||
5.135000000000000142e+01 2.888511147237640686e-01 | ||
5.454999999999999716e+01 3.117799609850196996e-01 | ||
7.390000000000000568e+01 2.596296621685815764e-01 | ||
2.269999999999999929e+01 5.027830353079761061e-02 | ||
2.794999999999999929e+01 -3.151681375235237774e-03 | ||
2.789999999999999858e+01 7.877458372718487700e-02 | ||
3.454999999999999716e+01 6.615319001556019263e-02 | ||
3.764999999999999858e+01 1.092985999992179269e-01 | ||
4.275000000000000000e+01 1.403562012299598427e-01 | ||
4.495000000000000284e+01 1.919179507721606448e-01 | ||
4.525000000000000000e+01 2.400006823941317202e-01 | ||
5.160000000000000142e+01 3.100291448405274042e-01 | ||
4.460000000000000142e+01 3.326581837297094557e-01 |
Oops, something went wrong.