This is another state-driven simulation.
The crux of the simulation:
This code simulates a small school campus with one teacher, one classroom,
and three students. The tearcher waits in the classroom until there are
enough students in the room to begin the class. The teacher then tries to
transfer knowledge to the student(s). At the end of the semester (3 days)
the teacher gives a test over all of the knowledge available and scores the
students performance.
Ths students go about their daily activities (sleep, goto class, study, go back
to the dorm). Students will get upset when they have to go back to the
dorm because they got to class to late (the classroom doors are closed when
the class is filled).
This exampe demonstraits more complex inteteractions than the earlier miners
example that can found on this site.
Some of the objects are too tightly coupled. You can loosen the coupling on
your own by creating objects to handle the coupled interactions.
Code:
<!doctype html>
<html>
<head>
<title>
A less-simple state-driven simulation
</title>
<style type='text/css'>
body {font-family:Tempus Sans ITC,Times New Roman,Times, Serif;
background: #23292e;
color:#c5c5c5;
margin: 20px;
}
textarea {
background: #e8ed90;
border: 1px solid #a9ae54;
padding: 15px;
width: 500px;
font-weight: bold;
font-size: 14px;
color: #7c7834;
}
td {padding-left:5px;}
#dormroom {width:100px;height:200px;background-color:navy;color:white;}
#commonroom {width:100px;height:200px;background-color:blue;color:white;}
#in_class {width:100px;height:200px;background-color:red;color:white;}
#goto_class {width:200px;height:75px;background-color:red;color:white;}
#goto_dorm {width:200px;height:75px;background-color:blue;color:white;}
h1 span {font:normal normal 14px/14px Tempus Sans ITC,Times New Roman,Times, Serif;}
</style>
<script type="text/javascript">
/*
state driven objects.
*/
var Campus ={ //this is an eviornment object. It coordinites objects actions.
classrooms:[],
test_day:false, //used to control testing
enter_room:function(s,c){ // students enter a class through this method.
for(var i=0;i<Campus.classrooms.length;i++){
if(Campus.classrooms[i].course_name==c){
if(Campus.classrooms[i].ready==false){
Campus.classrooms[i].add_student(s);
}else{
s.stamina=1;
s.symbol="@!#";
}
}
}
},
exit_room:function(s,c){ // students exit normally with this method
for(var i=0;i<Campus.classrooms.length;i++){
if(Campus.classrooms[i].coursename==c){
Campus.classrooms[i].drop_student(s);
}
}
},
add_room:function(c){ // a way to add rooms
Campus.classrooms.push(c);
},
remove_room:function(c){ // a way to remove rooms
for(var i in Campus.classrooms.length){
if(Campus.classrooms[i]==c){
Campus.classrooms.splice(i,1);
}
}
}
}
function ClassRoom(course_name,min) // there is no explicit doors variable in the code. closed doors is a concept.
{ // teaching only occurs after the classroom doors close.
if(min==null)min=1; // min is the minimun # of students before doors close.
this.days_to_test=3; // 3 day until testing starts
this.min=min;
this.course_name = course_name; // identifies the classroom
this.ready=false; // true when the doors close.
this.students=[];
this.add_student=function(s){ //only smooth way for a student to enter a room
this.students.push(s);
log("added "+s.name);
if(this.students.length>=this.min){
this.ready=true;
}
if(this.days_to_test<1){
this.ready=false;
}
}
this.drop_student=function(s){ //only smooth way for a student to exit a room
for(var i in this.students){
if(this.students[i]==s){
students.splice(i,1);
log("dropped "+s.name);
}
}
if(this.students.length<this.min){
this.ready=false;
}
}
this.end_class=function(){ // open the doors and kick every one out - not a smooth move.
for(var i=0;i<this.students.length;i++){
this.students.stamina=1;
}
this.students=[]; // roughly kick all students out of the classroom
this.ready=false; // classroom doors are open
log("dropped all");
}
this.teach=function(lesson){ // lesson is knowledge = {idea name: idea details}
for(var i=0;i<this.students.length;i++){
if(this.teachable(this.students[i])){
this.students[i].knowledge[lesson.name]=lesson[lesson.name]; // transfer knowledge to the student
}
}
}
this.teachable=function(s){ // controls student learning
if(s.intelligence>Math.random()*20) // simple-random 'did you get that?'
return true;
return false;
}
this.in_class=function(){ // count the number of students in the classroom
return this.students.length;
}
this.ask=function(n){ // the end of semester test
var r="<font size='+1'><u>Define '"+n+"'?</u></font><br>";
for(var i=0;i<this.students.length;i++){
if(this.students[i].knowledge[n]!=undefined){ // student knows the answer.
r+=this.students[i].name + ": "+this.students[i].knowledge[n]+"<br>";
this.students[i].score++;
}else{ // student has no clue.
if(Math.random()>.3){
r+=this.students[i].name + ": ???<br>";
}else{
r+=this.students[i].name + ": "+flippantQuess[Math.floor(Math.random()*flippantQuess.length)]+"<br>"
}
}
}
return r;
}
}
function Teacher(state,name,knowledge){ // the teacher object - transfers it knowledge to students
if(knowledge==null)knowledge=[];
this.name = name;
this.intelligence=Math.floor(Math.random()*10)+10;
this.knowledge=knowledge;
this.symbol="";
this.lesson=0;
this.students = 0;
this.teaching=false;
this.answers="";
this.reknown=0; // not used
this.hours_max=3;
this.classroom=null;
this.state=state;
}
function Student(state,name,knowledge){ // student objects - learns from a teacher
if(knowledge==null)knowledge=[];
this.symbol="";
this.name = name;
this.intelligence=Math.floor(Math.random()*10)+10;
this.knowledge=knowledge;
this.reknown=0; // not used
this.stamina = Math.floor(Math.random()*10);
this.fully_rested = Math.floor(Math.random()*10)+5;
this.learning=false;
this.score=0;
this.state=state;
}
var teacher_state={ // bunch of states. all teachers actions are cotrolled through states
waiting:function(){ // wait for the classroom to fill up - snoozing encouraged
this.statename="waiting";
this.symbol="Zzz";
if(this.classroom!=null){
if(this.classroom.days_to_test<1){
this.state=teacher_state.testing;
Campus.test_day=true;
}else if(this.classroom.ready){
this.state=teacher_state.teach;
}
}
},
teach:function(){ // try to transfer randomly selected knowledge to the students
this.statename="teach";
this.symbol="!";
if(this.lesson>this.hours_max){ // shutdown the class if over the union enforced max teaching hours
this.lesson=0;
this.state=teacher_state.end_class;
}else{ // teach a random lesson
var kn = this.knowledge[Math.floor(Math.random()*this.knowledge.length)];
this.classroom.teach(kn);
this.lesson++;
}
},
teach2:function(){ // try to transfer knowledge to students in an orderly manner - not used
this.statename="teach";
if(this.lesson>=this.knowledge.length){ // go through all the lessons
this.lesson=0;
this.state=teacher_state.end_class;
}else{
log("teaching");
this.classroom.teach(this.knowledge[this.lesson]);
this.lesson++;
}
},
end_class:function(){ // class is over. say bye. one day closer to the test.
this.statename="end_class";
this.symbol="Bye"
this.classroom.end_class();
this.classroom.days_to_test--;
this.state=teacher_state.waiting;
},
testing:function(){ // keep the noise down
this.statename="testing";
this.symbol = " shh";
log("("+this.classroom.in_class()+" in class)");
if(this.classroom.in_class()>2){ // wait for at least 3 students to show up
this.state=teacher_state.run_test;
}
},
run_test:function(){ // test time
this.statename="run_test";
this.symbol = "";
if(this.expando1==undefined){ // use the expand to make is one-off method
if(glb_questions>this.knowledge.length){ // don't trust global data
glb_questions=this.knowledge.length;
}
if(glb_questions<1){
glb_questions=1;
}
this.expando1=0;
this.answers="<font size='+1'>A "+glb_questions+" Question TEST:</font><br>" // the rest is assigning/answering test questions
for(var i=0;i<glb_questions;i++){
this.answers+=this.classroom.ask(this.knowledge[Math.floor(Math.random()*this.knowledge.length)].name) + "<br>";
}
this.answers+="Final Results:<br>";
for(var i=0;i<this.classroom.students.length;i++){
this.answers += this.classroom.students[i].name +": "+this.classroom.students[i].score+"/"+glb_questions+ " ("+Math.floor(100*this.classroom.students[i].score/glb_questions)+"%)<br>";
}
this.answers+="<hr>";
this.answers+="** Refresh the page to run again **";
glb_rungame=false;
}
}
}
var student_state={ // a bunch of states. all student actions are controlled through states
in_dorm_room:function(){ // a nice place to rest
this.statename="in_dorm_room";
this.symbol="Zzz";
this.stamina++;
if(this.stamina>this.fully_rested){
this.state = student_state.in_common_room;
}
},
in_common_room:function(){ // all students use the common room to come and go
this.statename="in_common_room";
this.symbol="";
if(this.stamina>this.fully_rested){
this.state = student_state.goto_class;
}else{
this.state = student_state.in_dorm_room;
}
},
goto_class:function(){ // going to class.
this.statename="goto_class";
this.symbol="hi ho";
this.state = student_state.in_class;
},
goto_dorm:function(){ // going back to the dorm.
this.statename="goto_dorm";
this.symbol="tired";
this.state = student_state.in_common_room;
},
in_class:function(){ // try to get into class
this.statename="in_class";
if(Campus.test_day==true){ // if its a test day, take a seat - doors stay open on test day.
this.symbol="...";
Campus.enter_room(this,"programming basics");
this.state = student_state.testing;
}else{ // otherwise try to get into class before the doors close
this.symbol="?"
if(this.learning==false){ // the doors are open. take a seat.
Campus.enter_room(this,"programming basics");
this.learning=true;
}
if(--this.stamina<1){ // too late. the doors are closed. go back to the dorm.
Campus.exit_room(this,"programming basics");
this.learning=false;
this.state = student_state.goto_dorm;
}
}
},
testing:function(){ // in a seat, ready for testing
this.statename="testing";
this.symbol="???"
}
}
// bunch of globals - lazy.
// each global represents a student state - for display purposes.
var in_dorm_room="";
var in_common_room="";
var goto_class=""
var in_class="";
var testing="";
var goto_dorm="";
function loop(){ // <- run the simulation
log("Days until test: "+teacher.classroom.days_to_test);
in_dorm_room="";in_common_room="";goto_class="";in_class=teacher.name+" "+teacher.symbol+"<br>";testing="";goto_dorm=""; //reset globals
for(var i=0;i<students.length;i++){
students[i].state(); // run the current state.
//the rest is junk for the display
if(students[i].statename=="in_dorm_room")in_dorm_room+="("+students[i].name+" "+students[i].symbol+")<br>";
if(students[i].statename=="in_class")in_class+="("+students[i].name+" "+students[i].symbol+")<br>";
if(students[i].statename=="in_common_room")in_common_room+="("+students[i].name+" "+students[i].symbol+")<br>";
if(students[i].statename=="testing")testing+="("+students[i].name+" "+students[i].symbol+")<br>";
if(students[i].statename=="goto_class")goto_class+="("+students[i].name+" "+students[i].symbol+")<br>";
if(students[i].statename=="goto_dorm")goto_dorm+="("+students[i].name+" "+students[i].symbol+")<br>";
}
teacher.state(); // run the current state
display();
if(glb_rungame){ // turn off the simulation after the test is complete.
setTimeout(loop,1000);
}
}
function display(){
var s = ""
for(var i=0;i<students.length;i++){
var n = students[i].name;
var k="";
for(var j in students[i].knowledge){
k+="("+j+") "
}
s+=n+" (Student "+i+")\t is " + students[i].statename + "...\r\n"
+ "intelligence = " + students[i].intelligence + "\r\n"
+ "stamina = " + students[i].stamina + "/"+students[i].fully_rested+"\r\n"
+ "learning = " + students[i].learning+ "\r\n"
+ "known = " + k + "\r\n" + "\r\n\r\n";
//+ "reknown = " + students[i].reknown + "\r\n\r\n";
}
document.getElementsByTagName("textarea")[0].value = s;
document.getElementById("dormroom").innerHTML=in_dorm_room;
document.getElementById("commonroom").innerHTML=in_common_room;
document.getElementById("goto_class").innerHTML=goto_class;
document.getElementById("in_class").innerHTML=in_class+testing;
document.getElementById("goto_dorm").innerHTML=goto_dorm;
document.getElementById("test").innerHTML=teacher.answers;
}
// a simple array of associations to teach
var knowledgePB = [
{"name":"value","value":"the current state of a concept which may varry"},
{"name":"constant","constant":"stores values that do not varry. A constant is a name for a stored non-varying value."},
{"name":"variable","variable":"stores values that can varry. A variable is a name for a stored value."},
{"name":"statement","statement":"performs a single action on a value"},
{"name":"loop","loop":"a control structure that orderly itterates through statement execution"},
{"name":"conditional","conditional":"a controlled furcation of statment exectution"},
{"name":"for","for":"a loop control structure. itteration is controlled via indexing variable test"},
{"name":"do","do":"a loop control structure. itteration is controlled via variable test"},
{"name":"if","if":"a bifurcation condtional control structure. bifurcation is controlled via value testing"},
{"name":"switch","switch":"a polyfurcation condtional control structure. polyfurcation is cotrolled via value testing"}
];
var glb_rungame=true; // used to control the simluation
var glb_questions=4; // the total number of question to ask in the test
var teacher = new Teacher(teacher_state.waiting,"Dr Bob",knowledgePB); // a state, a name, and a knowledge array
teacher.classroom = new ClassRoom("programming basics",2); // a cource name and the maxmimum number os students
Campus.add_room(teacher.classroom); // what is a teacher without a classroom - mom.
var students=[];
students[0] = new Student(student_state.in_dorm_room,"Fred"); // a states, a name , no knowlege to start off.
students[1] = new Student(student_state.in_dorm_room,"Mark");
students[2] = new Student(student_state.in_dorm_room,"Buddy");
var flippantQuess=["doh...","Your mama","kilroy was here","#@!","true"]; // what is said when there is no answer.
// let 'em loose.
window.onload=loop;
function log(n){
document.getElementById("info").innerHTML=n;
}
</script>
</head>
<body>
<div>Course: Programming Basics. Teacher: Dr Bob. <span id="info"> </span></div>
<h1>A less-simple state-driven simulation <span>(An automated campus)</span></h1>
<!-- bad style-table based display -->
<table><tr>
<td valign="top"><textarea rows="20" cols="40"></textarea></td>
<td valign="top">
<table>
<tr>
<td align="center">dorm room</td><td align="center">common room</td><td align="right">going to class =></td><td align="center">class room</td>
</tr><tr>
<td id='dormroom' rowspan="3"></td><td id="commonroom" rowspan="3"></td><td id="goto_class"></td><td id='in_class' rowspan="3"></td>
</tr><tr>
<td> </td>
</tr><tr>
<td id='goto_dorm'></td>
</tr></tr>
<td align="center">dorm room</td><td align="center">common room</td><td align="left"><= going to dorm</td><td align="center">class room</td>
</tr>
</table>
<div id="test"></div>
</td>
</tr></table>
</body>
</html>
tested on non-mobile windows platform.
tested in ie, ff, safari, chrome, opera