# File vw4.py
#
# Author: G.Doeben-Henisch
# First date: Aug 7, 2019
# Last date: Aug 19, 2019
###################################################
# PY USING AAI PARADIGMA
# APPLYING ACTOR-ACTOR STORY (AAS) and
# ACTOR MODELING (AM) to the
# VIRTUAL WORLD APPLICATION
###################################################
###################################################
# SEE ACCOMPANYING VISUAL PRESENTATION
# OF AAS AND AM INTERACTION
###################################################
###################################################
# GENERAL OVERVIEW Level 0
##################################################
'''
As you can see in the accompanying diagram for the vw4.py source code there exists a hierarchy of AAS with AM(aA1) as a member and again an AAS embedded in AM(aA1) with AM(A) as a member.
The first AAS at level 0 describes the interactions between the human user as an executing actor (eA) and a virtual world as an assisting actor (aA). Both actors are embedded in a communication relation COM.
'''
###################################################
# GENERAL OVERVIEW Level -1
###################################################
'''
- The behavior function of the aA1 actor consists of two components: one component 'Com' interacts with the outside world AAS level 0, the other component 'Run' manages the internal 2D-space with its objects and
their possible behavior in time.
- The inner structure of the virtual world actor labeled aA1 allows the description of another actor-actor story (AAS) one level below 0 called level -1.
This AAS is hidden from the outside of the virtual world actor 'aA1'.
- This embedded AAS describes a 2-dimensional space SP with different kinds of objects. One kind of objects are the 'A'-labeled objects and a communication buffer CBF.
The Run-function of the virtual world actor aA1 can read and write the communication buffere CBF and can thereby use information from the embedded actor A to organize the behavior of the 2D-space.
By writing into the communication buffer CBF the virtual world actor aA1 can influence the behaviro of the embedded actor A.
- One of the objects of the 2D-space -- the A-objects -- are again actors with assumed inputs and outputs.
One can define an actor model for these actors which are elements of AAS level -1.
- The in aA1 embedded actors A can receive messages from a communication buffer CBF within AAS level -1 and they can send messages to this communication buffer CBF.
'''
############################
# IMPORTS
import random as rnd
import copy as cp
import vwmanager as vw
############################
# SUPPORTING FUNCTION
#
# Now in a dedicated module vwmanager.py in folder 'code'
# Complete path: C:\Users\gerd_2\code
# This path has to be included for the python path search mechanism
#
# Show actual path values
#
# >>> import sys
#>>> sys.path
# ...
# Extend the python path by appending the modul path:
#
# In my case the path is as follows:
#
#>>> sys.path.append('C:\\Users\\gerd_2\\code')
##########################
# Main Programm
#
###############
# MAIN
#
# The loop will work as long as the value of the variable 'loop' is different to 'N'
# The loop represents the sequence of interactions between the user as executive actor and the virtual world as assisting actor.
#
# While running the loop the virtual world actor generates some internal data structures which then will be used for the management of an internal,
# embedded actor-actor story on level -1.
#
# GLOBAL PROPERTIES OF SOME OBJECTS used in the 2D-space of the virtual world
#
objL={'F':[0,1000, 100], 'A':[0,1000,100,500,0]}
#############################################
# The global objekt list objL contains properties for:
# A food object 'F' which has an initial value of '1000' and can grow if below 1000 by 20 every cycle
# An actor object 'A' which has an ID, an initial energy level of 1000, loses every cycle 200 energy units
# independent of moving or not moving, and gains 500 energy units by eating
# and a direction DIR=0
# These values will be attached to a new object generated in the 2D-space and can easily be changed for experimental reasons.
###########################################
'''
The interactions in the AAS level 0 are the following ones:
- The user will be asked from the virtual world actor:
- how many 'Number of columns (= equal to rows!)' shall be realized
- how large the 'Percentage (as integer) of obstacles in the 2D-grid' should be
- how large the 'Percentage (as integer) of Energy Objects (= Food) in the 2D-grid' should be
- how large ther 'Percentage (as integer) of Actor Objects in the 2D-grid' should be.
- After this input the virtual world will generate a 2D-world with the entered dimensions and it is showing objects as 'O', food as 'F', spaces as '_', and actors as 'A'.
- Then the user will be asked how man cycles (CYC) the world shall run.
- If the number of wanted cycles (CYC) has been reached the user will be asked to stop the run (=N) or to continue (=Y).
- During the preparation phase the following global data structure will be generated, which later in the loop will be used:
- mx := the 2D-world matrix
- plO := the list of all obstacle objects
- olF := the list of all food objects
- olA := the list of all actor objects
'''
loop='Y'
while loop!='N':
###################################################################################
# ASKING USER FOR SOME PARAMETERS TO START
##################################################################################
# Ask for mode of information given by the system
# say = 1 asks for maximal information, say = 0 gives only minimal nformation
say = int(input('Amount of information: 1 is maximum, 0 is minimum'))
# Number of columns (m) and rows (n); by default equal.
m=int(input('Number of columns (= equal to rows!) of 2D-grid ?'))
n=m
mx0=vw.nmlist(n) # Generate a first empty matrix n*m
mx = cp.deepcopy(mx0)
# Percentage % of obstacles in the 2D-world?\n)
obstacles=int(input('Percentage (as integer) of obstacles in the 2D-grid?'))
no=vw.numberObj(obstacles,n)
mx=vw.placeObjs(no,n,mx,'O',say)
# Percentage % of energy objects in the 2D-world?\n)
energyObj=int(input('Percentage (as integer) of Food Objects in the 2D-grid ?'))
no=vw.numberObj(energyObj,n)
mx=vw.placeObjs(no,n,mx,'F',say)
# Percentage % of actor objects in the 2D-world?\n)
actorObj=int(input('Percentage (as integer) of Actor Objects in the 2D-grid ?'))
no=vw.numberObj(actorObj,n)
mx=vw.placeObjs(no,n,mx,'A',say)
# List of actual obstacles in the grid
c='O'
olO=vw.makeobjL(c,objL,mx,n)
name='obstacles'
print('\n Objects as ',name,'\n')
vw.printMX(olO)
# List of actual food objects in the grid:
c='F'
olF=vw.makeobjL(c,objL,mx,n)
name='food'
print('\n Objects as ',name,'\n')
vw.printMX(olF)
# List of actual actor objects in the grid:
c='A'
olA=vw.makeobjL(c,objL,mx,n)
name='actor'
print('\n Objects as ',name,'\n')
vw.printMX(olA)
vw.printMX(mx)
print('\n END OF PREPARATION\n')
print('WORLD CYCLE STARTS\n')
# Final computation of real percentages of objects
# Because in the generation of new objects the already generated objects can be overwritten
# these already existing ones can become diminished
# 100% = n x n
print('----------------------------------------------------')
print('Real percentage of obstacles = ',len(olO)/((n*n)/100))
print('Real percentage of food = ',len(olF)/((n*n)/100))
print('Real percentage of actors = ',len(olA)/((n*n)/100))
print('----------------------------------------------------')
###########################################################
# WORLD INTERNAL RUN STARTS
# ##########################################################
# COUNTING CYCLES (CYY)
CYC=int(input('How many CYCLES do you want?'))
stepMode = int(input('Single Step = 1 or Continous = 0?'))
WCLCK=0
for t in range(CYC):
print('Length of olA',len(olA))
if len(olA)<1:
break
# Show actual state of the GRID
# All object lists (olO, olF, olA) will be read and visualized in a 2D-Grid
# It is assumed that these lists represent the actual state of affairs.
mx = vw.show2D(mx0, olO, olF, olA)
print('\n-----------------------------------------------------\n')
print('WORLD AT CYCLE = ',WCLCK,'\n')
vw.printMX(mx)
if stepMode == 1:
cont = 'x'
while cont != 'c':
cont = input('Press key c for continuation!')
# Generating new directions for move
olA = vw.randDir(olA)
if say == 1:
print('New directions for actor objects:\n')
vw.printMX(olA)
else:
pass
# Compute positions in new virtual copy olAA from olA
# The list olAA will be used to check wether the new positions are in agreement with the
# 'laws' of the virtual world as managed by the actor model of the virtual world.
# This checking proceeds in several individual steps. Every element whose planned
# new position will be identified as 'not in agreement' will be replaced by the original
# position (still available in the olA list) and marked with a '-1' direction.
olAA=vw.newPos0s(olA,n,say)
if say ==1:
print('New positions for actor objects in olAA:\n')
vw.printMX(olAA)
print('Compare with old list olA \n')
vw.printMX(olA)
else:
pass
# Now starts a series of operations to eleiminate all impossible moves
# In a first step it will be checked whether there is a collision between
# the given objects and the new planned positions.
# For this a DISALLOW-list will be generated for those actors which would cause collisions.
disallowL = vw.reduceAOs(mx0,olO, olF, olAA,olA,say)
# With the aid of disallowL will all the actors in olAA be replaced with
# the actors and old positions from olA
olAA = vw.updateOLAs(olA,olAA,disallowL,say)
# Next step:
# Check of similar positions in olAA and return the indizes of the similar pairs
# in the list equalL
equalL = vw.selfLs(olAA,say)
if say == 1:
print('Indizes of equal elements\n')
vw.printMX(equalL)
else:
pass
# Again will the actual list olAA with the conflicting actors be replaced
# by the actors from olA with the aid of equalL
olAA = vw.restoreAAs(equalL, olA, olAA,say)
# In the next step:
# All actors pointing to new positions which are free (indicated by '_' in the grid)
# and which are depending from the directions {2,4,6,8} are only reachable if the two
# neighbor cells generated by the directions D-1 and D+1 are free too. Therefore
# one has to check whether this is the case.
# As previously a disallowL list will be generated and then a reduceAO() and updateOLA()
# will be applied.
disallowL = vw.checkCornerYX(olAA,mx)
if say == 1:
print('disallowL after corner cell checks')
vw.printMX(disallowL)
else:
pass
# With the aid of disallowL will all the actors in olAA be replaced with
# the actors and old positions from olA
olAA = vw.updateOLAs(olA,olAA,disallowL,say)
# Then reload the olAA list so far into olA for further processing in the normal loop
olA = cp.deepcopy(olAA)
# Now all actors have reached their new position (or kept their old position).
# Finally it is assumed that all actors which can will consume food if available in their cell.
# energy = (olA, olF)
energy = vw.eating(olA, mx, olF)
print('EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n')
print('Updated energy levels in olF and olA')
vw.printMX(olA)
vw.printMX(olF)
# UPDATING all FOOD objects
# Take the list of all food objects olF
# Check whether the energy level (objL index 1) is below Maximum.
# If YES add some amount (objL Index 2).
# If not do nothing
olF=vw.foodUpdate(olF,objL)
if say == 1:
print('Updated food objects:\n')
vw.printMX(olF)
else:
pass
# UPDATING all ACTOR objects
# Take the list of all actor objects olA
# REDUCE the energy level according to the standard
# CHECK whether the energy level is below 0.
# If YES add REMOVE AVATAR from grid.
olA=vw.actorUpdates(olA,objL,mx,say)
if say == 1:
print('Updated actor objects:\n')
vw.printMX(olA)
else:
pass
# Show summary of all changes in a new 2D-grid
WCLCK+=1 # Main reference point for time
# Clarify how to continue (MAIN LOOP!)
loop=input("\n MAIN LOOP: STOP = 'N', CONTINUE != 'N' \n")