Related
I am asking for your help.
I have an existing dag that cleans up airflow_db from our production airflow db. Now I need to adjust the code and add another connection to our test environment, I was advised to create a for cycle like
for conn_id in {"prod_db", "test_db"}:
with DAG( ... conn_id = conn_id, ...)
But I am not sure how to do that really, as I did the change the syntax is incorrect now.
Do you have any example/hint how to do that?
for conn_id in {"prod_db","test_db"}:
with DAG(
conn_id = conn_id,
dag_id="airflow_db_cleanup",
default_args={
"owner": "our team,
"email": mail_mail_mail,
"email_on_failure": True,
"email_on_retry": False,
"start_date": datetime(2022, 1, 1, 0, 0, 0),
"retries": 1,
"retry_delay": timedelta(minutes=1),
},
schedule_interval=timedelta(days=1),
max_active_runs=1,
) as dag,
dag.doc_md = doc
etc-then various functions that create sessions follow.
The operator here:
def get_max_db_entry_age_in_days(dag_run_conf):
max_db_entry_age_in_days = None
default_max_db_entry_age_in_days = int(Variable.get("max_db_entry_age_in_days", 90))
if dag_run_conf:
max_db_entry_age_in_days = dag_run_conf.get("maxDBEntryAgeInDays", None)
logging.info("maxDBEntryAgeInDays from dag_run.conf: " + str(dag_run_conf))
if max_db_entry_age_in_days is None:
logging.info(
"maxDBEntryAgeInDays conf variable isn't included. Using Default '"
+ str(default_max_db_entry_age_in_days)
+ "'"
)
max_db_entry_age_in_days = default_max_db_entry_age_in_days
return max_db_entry_age_in_days
def cleanup_function(**context):
dag_run = context.get("dag_run")
dag_run_conf = getattr(dag_run, "conf", None)
max_db_entry_age_in_days = get_max_db_entry_age_in_days(dag_run_conf)
execution_date = context.get("execution_date")
max_date = execution_date + timedelta(-max_db_entry_age_in_days)
dry_run = Variable.get("dry_run", "True") == "True"
airflow_db_model = context["params"].get("airflow_db_model")
age_check_column = context["params"].get("age_check_column")
keep_last_run = context["params"].get("keep_last_run")
dag_id = context["params"].get("dag_id")
cascade_delete = context["params"].get("cascade_delete")
with create_session(conn_id) as session:
logging.info("Configurations:")
logging.info("max_date: " + str(max_date))
logging.info("session: " + str(session))
logging.info("airflow_db_model: " + str(airflow_db_model))
logging.info("age_check_column: " + str(age_check_column))
logging.info("keep_last_run: " + str(keep_last_run))
logging.info("dag_id: " + str(dag_id))
logging.info("")
logging.info("Running Cleanup Process...")
query = get_main_query(
session, airflow_db_model, age_check_column, keep_last_run, dag_id, max_date
)
entries_to_delete_count = query.count()
logging.info(f"Query : {query}")
logging.info(
f"Will be deleting {entries_to_delete_count} {airflow_db_model.__name__}"
)
logging.info(list(query.values(dag_id)))
if cascade_delete:
for child in cascade_delete:
child_model = child.get("child_model")
child_id = child.get("child_id")
parent_id = child.get("parent_id")
delete_query = get_cascade_delete_query(
session, query, child_model, child_id, parent_id
)
child_rows = delete_query.count()
logging.info(
f"Going to delete {child_rows} rows of {child_model.__name__}."
)
logging.info(list(delete_query.values(child_id)))
if not dry_run:
delete_query.delete(synchronize_session=False)
if dry_run:
logging.info("Dry run finished")
else:
logging.info("Performing main delete...")
query.delete(synchronize_session=False)
session.commit()
logging.info("Cleanup process finished")
for db_object in DATABASE_OBJECTS:
cleanup_op = PythonOperator(
task_id="cleanup_" + str(db_object["airflow_db_model"].__name__),
python_callable=cleanup_function,
params=db_object,
provide_context=True,
dag=dag,
)
def remove_inactive_dags_from_ui(**context):
max_db_age_inactive_dags = int(Variable.get("max_db_age_inactive_dags", 7))
dry_run = Variable.get("dry_run", "True") == "True"
threshold_date = context["execution_date"].subtract(days=max_db_age_inactive_dags)
with create_session(conn_id) as session:
query = (
session.query(DagModel)
.filter(
and_(
DagModel.is_active.is_(False),
DagModel.is_subdag.is_(False),
DagModel.last_parsed_time <= threshold_date,
)
)
.options(load_only(DagModel.dag_id))
)
inactive_dags = query.all()
logging.info(f"Will be deleted {query.count()} dags")
for inactive_dag in inactive_dags:
logging.info(f"Deleting dag {inactive_dag.dag_id} from UI")
if not dry_run:
delete_dag(inactive_dag.dag_id, session=session)
if dry_run:
logging.info("Dry run finished")
else:
session.commit()
logging.info("Cleanup process finished")
remove_inactive_dags_task = PythonOperator(
task_id="remove_inactive_dags_from_ui",
python_callable=remove_inactive_dags_from_ui,
provide_context=True,
dag=dag,
)
Creating a loop of DAGs or Tasks is bad practice inside Airflow, I'm not sure if it's even possible.
The correct approach would be to have two tasks that run the same DAG code, but passing a different connection through each time. You could even parallel these tasks up so both DBs are cleared down at the same time rather than one after another.
I wanted to report some debug information for a parser I am writing in lua. I am using the debug hook facility for tracking but it seems like there is some form of race condition happening.
Here is my test code:
enters = 0
enters2 = 0
calls = 0
tailcalls = 0
returns = 0
lines = 0
other = 0
exits = 0
local function analyze(arg)
enters = enters + 1
enters2 = enters2 + 1
if arg == "call" then
calls = calls + 1
elseif arg == "tail call" then
tailcalls = tailcalls + 1
elseif arg == "return" then
returns = returns + 1
elseif arg == "line" then
lines = lines + 1
else
other = other + 1
end
exits = exits + 1
end
debug.sethook(analyze, "crl")
-- main code
print("enters = ", enters)
print("enters2 = ", enters2)
print("calls = ", calls)
print("tailcalls = ", tailcalls)
print("returns = ", returns)
print("lines = ", lines)
print("other = ", other)
print("exits = ", exits)
print("sum = ", calls + tailcalls + returns + lines + other)
and here is the result:
enters = 429988
enters2 = 429991
calls = 97433
tailcalls = 7199
returns = 97436
lines = 227931
other = 0
exits = 430009
sum = 430012
Why does none of this add up? I am running lua 5.4.2 on Ubuntu 20.04, no custom c libraries, no further messing with the debug library.
I found the problem...
The calls to the print function when printing the result also trigger the hook, which only affects the results that have not been printed yet.
I have used this website over a hundred times and it has helped me so much with my coding (in python, arduino, terminal commands and Window's prompt). I thought I would put up some knowledge that I found, for things that Stack overflow could not help me with but my be helpful for others in a similar situation. So have a look at the code below. I hope if helps people with creating their own backup code. I am most proud with the "while '\r\n' in output" part of the below code. :
output = child0.readline()
while '\r\n' in output:
msg.log(output.replace('\r\n', ''), logMode + 's')
output = child0.readline()
This helps find the EOF when the program has finished running. Hence you can output the terminal program's output as the program is running.
I will be adding a Windows version to this code too. Possibly with robocopy.
Any questions with the below code, please do not hesitate to ask. NB: I changed people's names and removed my username and passwords.
#!/usr/bin/python
# Written by irishcream24, amateur coder
import subprocess
import sys
import os.path
import logAndError # my own library to handle errors and log events
from inspect import currentframe as CF # help with logging
from inspect import getframeinfo as GFI # help with logging
import threading
import fcntl
import pexpect
import time
import socket
import time as t
from sys import platform
if platform == "win32":
import msvcrt
portSearch = "Uno"
portResultPosition = 1
elif platform == "darwin":
portSearch = "usb"
portResultPosition = 0
else:
print 'Unknown operating system'
print 'Ending Program...'
sys.exit()
# Check if another instance of the program is running, if so, then stop second.
pid_file = 'program.pid'
fp = open(pid_file, 'w')
try:
fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
# another instance is running
print "Program already running, stopping the second instance..."
sys.exit(1)
# Determine where main program files are stored
directory = os.path.dirname(os.path.realpath(__file__))
# To print stderr to both screen and file
errCounter = 0
exitFlag = [0]
class tee:
def __init__(self, _fd1, _fd2):
self.fd1 = _fd1
self.fd2 = _fd2
def __del__(self):
if self.fd1 != sys.stdout and self.fd1 != sys.stderr :
self.fd1.close()
if self.fd2 != sys.stdout and self.fd2 != sys.stderr :
self.fd2.close()
def write(self, text):
global errCounter
global exitFlag
if errCounter == 0:
self.fd1.write('%s: ' %t.strftime("%d/%m/%y %H:%M"))
self.fd2.write('%s: ' %t.strftime("%d/%m/%y %H:%M"))
errCounter = 1
exitFlag[0] = 1
self.fd1.write(text)
self.fd2.write(text)
def flush(self):
self.fd1.flush()
self.fd2.flush()
# Error and log handling
errMode = 'pf' # p = print to screen, f = print to file, e = end program
errorFileAddress = '%s/errorFile.txt' %directory
outputlog = open(errorFileAddress, "a")
sys.stderr = tee(sys.stderr, outputlog)
logFileAddress = '%s/log.txt' %directory
logMode = 'pf' # p = print to screen, f = print to file
msg = logAndError.logAndError(errorFileAddress, logFileAddress)
# Set computer to be backed up
sourceComputer = 'DebbieMac'
try:
sourceComputer = sys.argv[1]
except:
print 'No source argument given.'
if sourceComputer == 'SamMac' or sourceComputer == 'DebbieMac' or sourceComputer == 'mediaCentre' or sourceComputer == 'garageComputer':
pass
else:
msg.error('incorrect source computer supplied!', errMode, GFI(CF()).lineno, exitFlag)
sys.exit()
# Source and destination setup
backupRoute = 'network'
try:
backupRoute = sys.argv[2]
except:
print 'No back up route argument given.'
if backupRoute == 'network' or backupRoute == 'direct' or backupRoute == 'testNetwork' or backupRoute == 'testDirect':
pass
else:
msg.error('incorrect backup route supplied!', errMode, GFI(CF()).lineno, exitFlag)
sys.exit()
# Source, destination and exclude dictionaries
v = {
'SamMac network source' : '/Users/SamJones',
'SamMac network destination' : '/Volumes/Seagate/Sam_macbook_backup/Backups',
'SamMac direct source' : '/Users/SamJones',
'SamMac direct destination' : '/Volumes/Seagate\ Backup\ Plus\ Drive/Sam_macbook_backup/Backups',
'SamMac testNetwork source' : '/Users/SamJones/Documents/Arduino/arduino_sketches-master',
'SamMac testNetwork destination' : '/Volumes/Seagate/Sam_macbook_backup/Arduino',
'SamMac exclude' : ['.*', '.Trash', 'Library', 'Pictures'],
'DebbieMac network source' : '/Users/DebbieJones',
'DebbieMac network destination' : '/Volumes/Seagate/Debbie_macbook_backup/Backups',
'DebbieMac direct source' : '/Users/DebbieJones',
'DebbieMac direct destination' : '/Volumes/Seagate\ Backup\ Plus\ Drive/Debbie_macbook_backup/Backups',
'DebbieMac testNetwork source': '/Users/DebbieJones/testFolder',
'DebbieMac testNetwork destination' : '/Volumes/Seagate/Debbie_macbook_backup',
'DebbieMac testDirect source' : '/Users/DebbieJones/testFolder',
'DebbieMac testDirect destination' : '/Volumes/Seagate\ Backup\ Plus\ Drive/Debbie_macbook_backup',
'DebbieMac exclude' : ['.*', '.Trash', 'Library', 'Pictures']
}
# Main threading code
class mainThreadClass(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
PIDMessage = 'Starting backup PID: %s'%os.getpid()
msg.log(PIDMessage, logMode)
mainThread()
msg.log('Process completed successfully\n', logMode)
def mainThread():
if platform == "win32":
pass
elif platform == "darwin":
if 'network' in backupRoute:
# Connect to SeagateBackup
if os.path.ismount('/Volumes/Seagate') == False:
msg.log('Mounting Seagate Backup Hub', logMode)
commandM = 'mount volume'
smbPoint = '"smb://username:password#mediacentre/Seagate"'
childM = pexpect.spawn("%s '%s %s'" %('osascript -e', commandM, smbPoint), timeout = None)
childM.expect(pexpect.EOF)
else:
msg.log('Seagate already mounted', logMode)
# Use rsync to backup files
commandR = 'rsync -avb '
for s in v['%s exclude' %sourceComputer]:
commandR = commandR + "--exclude '%s' " %s
commandR = commandR + '--delete --backup-dir="../PreviousBackups/%s" ' %time.strftime("%d-%m-%y %H%M")
commandR = commandR + '%s %s' %(v['%s %s source' %(sourceComputer, backupRoute)], v['%s %s destination' %(sourceComputer, backupRoute)])
msg.log(commandR, logMode)
msg.log('Running rsync...rsync output below', logMode)
child0 = pexpect.spawn(commandR, timeout = None)
# Handling command output
# If no '\r\n' in readline() output, then EOF reached
output = child0.readline()
while '\r\n' in output:
msg.log(output.replace('\r\n', ''), logMode + 's')
output = child0.readline()
return
if __name__ == '__main__':
# Create new threads
threadMain = mainThreadClass()
# Start new Threads
threadMain.start()
logAndError.py
# to handle errors
import time
import sys
import threading
class logAndError:
def __init__(self, errorAddress, logAddress):
self.errorAddress = errorAddress
self.logAddress = logAddress
self.lock = threading.RLock()
def error(self, message, errMode, lineNumber=None, exitFlag=[0]):
message = '%s: %s' %(time.strftime("%d/%m/%y %H:%M"), message)
# p = print to screen, f = print to file, e = end program
if 'p' in errMode:
print message
if 'f' in errMode and 'e' not in errMode:
errorFile = open(self.errorAddress, 'a')
errorFile.write('%s\n' %message)
errorFile.close()
return
def log(self, logmsg, logMode):
with self.lock:
logmsg2 = '%s: %s' %(time.strftime("%d/%m/%y %H:%M"), logmsg)
if 'p' in logMode:
# s = simple (no date stamp)
if 's' in logMode:
print logmsg
else:
print logmsg2
if 'f' in logMode:
if 's' in logMode:
logFile = open(self.logAddress, 'a')
logFile.write('%s\n' %logmsg)
logFile.close()
else:
logFile = open(self.logAddress, 'a')
logFile.write('%s\n' %logmsg2)
logFile.close()
return
I have got a problem in running the blew program for reading a text file as input file. This input file is like a matrix with 3 columns -- 3 numbers with format of (3(F3.6,1x)) -- and 4368 rows.
The input file is:
602340.440000 129706.190000 28.892939
602340.880000 129706.390000 28.955128
602884.500000 128780.700000 29.876873
602884.380000 128781.190000 29.875114
602884.250000 128781.660000 29.885448
602884.130000 128782.150000 29.895996
602883.940000 128782.630000 29.899380
602883.810000 128783.120000 29.903221
602883.690000 128783.590000 29.907070
PROGRAM is:
USE BIEF
USE DECLARATIONS_TELEMAC2D
IMPLICIT NONE
INTEGER LNG,LU, ITRAC,I, NSOM,J, K, NDOWN
INTEGER, PARAMETER :: NLINE =4368
DOUBLE PRECISION, PARAMETER:: BATHY_RADIER_up= 29.84D0
DOUBLE PRECISION, PARAMETER:: DEPTH_up = 2.15D0
REAL :: A(5000),B(5000),C(5000)
DOUBLE PRECISION :: XPOLYD(14), YPOLYD(14), INPOLYD(14)
COMMON/INFO/LNG,LU
DOUBLE PRECISION XPOLY(6), YPOLY(6),COTE_RADIER_up
NSOM = 6
XPOLY(1) = 602883.13
XPOLY(2) = 602886.15
XPOLY(3) = 602887.15
XPOLY(4) = 602905.46
XPOLY(5) = 602902.52
XPOLY(6) = 602884.13
YPOLY(1) = 128779.99
YPOLY(2) = 128780.80
YPOLY(3) = 128777.12
YPOLY(4) = 128741.21
YPOLY(5) = 128739.75
YPOLY(6) = 128775.96
AT = 0.D0
CALL OS( 'X=0 ' , X=U )
CALL OS( 'X=0 ' , X=V )
IF(CDTINI(1:10).EQ.'COTE NULLE'.OR.
* CDTINI(1:14).EQ.'ZERO ELEVATION') THEN
CALL OS( 'X=C ' , H , H , H , 0.D0 )
CALL OS( 'X=X-Y ' , H , ZF , H , 0.D0 )
ELSEIF(CDTINI(1:14).EQ.'COTE CONSTANTE'.OR.
* CDTINI(1:18).EQ.'CONSTANT ELEVATION') THEN
CALL OS( 'X=C ' , H , H , H , COTINI )
CALL OS( 'X=X-Y ' , H , ZF , H , 0.D0 )
ELSEIF(CDTINI(1:13).EQ.'HAUTEUR NULLE'.OR.
* CDTINI(1:10).EQ.'ZERO DEPTH') THEN
CALL OS( 'X=C ' , H , H , H , 0.D0 )
ELSEIF(CDTINI(1:17).EQ.'HAUTEUR CONSTANTE'.OR.
* CDTINI(1:14).EQ.'CONSTANT DEPTH') THEN
CALL OS( 'X=C ' , H , H , H , HAUTIN )
ELSEIF(CDTINI(1:13).EQ.'PARTICULIERES'.OR.
* CDTINI(1:10).EQ.'PARTICULAR'.OR.
* CDTINI(1:07).EQ.'SPECIAL') THEN
NDOWN = 14
XPOLYD(1) = 602883.13
XPOLYD(2) = 602886.15
XPOLYD(3) = 602864.47
XPOLYD(4) = 602837.90
XPOLYD(5) = 602821.91
XPOLYD(6) = 602649.77
XPOLYD(7) = 602634.35
XPOLYD(8) = 602345.08
XPOLYD(9) = 602326.07
XPOLYD(10) = 602619.31
XPOLYD(11) = 602638.33
XPOLYD(12) = 602811.64
XPOLYD(13) = 602831.52
XPOLYD(14) = 602857.16
YPOLYD(1) = 128779.99
YPOLYD(2) = 128780.80
YPOLYD(3) = 128867.74
YPOLYD(4) = 128936.74
YPOLYD(5) = 128953.95
YPOLYD(6) = 129105.43
YPOLYD(7) = 129143.43
YPOLYD(8) = 129713.38
YPOLYD(9) = 129708.26
YPOLYD(10) = 129136.41
YPOLYD(11) = 129094.72
YPOLYD(12) = 128941.16
YPOLYD(13) = 128931.09
YPOLYD(14) = 128865.81
PRINT *, 'opening file'
DO 10 J=1,NPOIN
IF(INPOLY(X(J),Y(J),XPOLY,YPOLY,NSOM)) THEN
PRINT *, 'upstream area'
H%R(J)=MAX(0.D0,COTE_RADIER_up-ZF%R(J))
U%R(J)=0.0D0
PRINT *, 'upstream area'
write(lu,*) 'upstream ....',J,H%r(J)
ELSE
IF(INPOLY(X(J),Y(J),XPOLYD,YPOLYD,NDOWN)) THEN
OPEN(unit =90, FILE = 'cunnette_xyz.txt', FORM='FORMATTED')
PRINT *, 'downstream area'
READ(90,*) A(K),B(K),C(K)
PRINT *, 'already read'
DO K=1,NLINE
PRINT *, "number of lines read:", NLINE
IF(A(K).EQ.X(J).AND.B(K).EQ.Y(J)) then
PRINT *, 'Nodes are inside'
H%R(K)=0.45D0
U%R(K)=0.D0
ELSE
H%R(K)=0.0D0
U%R(K)=0.0D0
ENDIF
ENDDO
CLOSE(90)
ENDIF
ENDIF
10 CONTINUE
ELSE
IF(LNG.EQ.1) THEN
WRITE(LU,*) 'CONDIN : CONDITION INITIALE NON PREVUE : ',CDTINI
ENDIF
IF(LNG.EQ.2) THEN
WRITE(LU,*) 'CONDIN: INITIAL CONDITION UNKNOWN: ',CDTINI
ENDIF
STOP
ENDIF
IF(NTRAC.GT.0) THEN
DO ITRAC=1,NTRAC
CALL OS( 'X=C ' , X=T%ADR(ITRAC)%P , C=TRAC0(ITRAC) )
ENDDO
ENDIF
CALL OS( 'X=C ' , VISC , VISC , VISC , PROPNU )
RETURN
END
The error message when running is:
at line read file: Fortran runtime error: End of file.
My last output is 'downstream area' aftre open Command. Could anybody help me please?
First of all, you are opening the file repeatedly for every value of J. This seems wrong.
Secondly, you have not given the variable NPOIN a value (greater than 1), so your outer loop on J will not stop running (I guess), and this will eventually lead to you trying to read beyond the end of file.
You have nested loops, one on J and one on K, which seems not logical. You should have one only.
Open the file outside of the J loop, and then read line per line until you reach the end of the file:
INTEGER :: Reason
INTEGER :: NPOIN
OPEN(unit =90, IOSTAT=Reason, FILE = 'cunnette_xyz.txt', FORM='FORMATTED')
IF (Reason > 0) THEN
* ... something wrong ...
PRINT *, 'Error opening file'
STOP
END IF
NPOIN = 0
DO
NPOIN = NPOIN + 1
READ(*,*, IOSTAT=Reason) A(NPOIN), B(NPOIN), C(NPOIN)
IF (Reason > 0) THEN
PRINT *, 'Error reading input from file. Aborted'
STOP
ELSE IF (Reason < 0) THEN
* ... end of file reached ...
NPOIN = NPOIN - 1
PRINT *, 'All data read from file'
EXIT
END IF
END DO
* All input should be in A, B, C arrays now, with NPOIN entries
DO K=1, NPOIN
* Your processing comes here. No more file I/O.
END DO
I am attempting to write a tic-tac-toe game in lua, and plan on using the minimax algorithm to decide non-human moves. The first step in this involves generating a tree of all possible board states from a single input state. I am trying to recursively do this, but cannot seem to figure out how. (I think) I understand conceptually how this should be done, but am having trouble implementing it in lua.
I am trying to structure my tree in the following manner. Each node is a list with two fields.
{ config = {}, children = {} }
Config is a list of integers (0,1,2) that represent empty, X, and O and defines a TTT board state. Children is a list nodes which are all possible board states one move away from the current node.
Here is my function that I currently have to build the game tree
function tree_builder(board, player)
supertemp = {}
for i in ipairs(board.config) do
--iterate through the current board state.
--for each empty location create a new node
--representing a possible board state
if board.config[i] == 0 then
temp = {config = {}, children = {}}
for j in ipairs(board.config) do
temp.config[j] = board.config[j]
end
temp.config[i] = player
temp.children = tree_builder(temp, opposite(player))
supertemp[i] = temp
end
end
return supertemp
end
The function is called in the following manner:
start_board = {config = {1,0,0,0}, children = {} }
start_board.children = tree_builder(start_board, 1)
When I comment out the recursive element of the function (the line "temp.children = builder(temp, opposite(player))") and only generate the first level of children. the output is correct. When called via code that is conceptually identical to (I am using love2D so formatting is different):
for i in pairs(start_board.children) do
print (start_board.children[i].config)
The three children are:
1,1,0,0
1,0,1,0
1,0,0,1
However, once I add the recursive element, the following is output for the same three children
1,1,2,1
1,1,2,1
1,1,2,1
I have been searching online for help and most of what I have found is conceptual in nature or involves implementation in different languages. I believe I have implemented the recursive element wrongly, but cannot wrap my head around the reasons why.
Don't understand what opposite(player) means in temp.children = tree_builder(temp, opposite(player)).
Notice that a recursion need an end condition.
This is my solution under your structure:
local COL = 3
local ROW = 3
local function printBoard( b )
local output = ""
local i = 1
for _,v in ipairs(b.config) do
output = output .. v .. ( (i % COL == 0) and '\n' or ',' )
i = i + 1
end
print( output )
end
local function shallowCopy( t )
local t2 = {}
for k,v in pairs(t) do
t2[k] = v
end
return t2
end
local MAX_STEP = COL * ROW
local ING = 0
local P1 = 1
local P2 = 2
local TIE = 3
local STATUS = { [P1] = "P1 Win", [P2] = "P2 Win", [TIE] = "Tied" }
local start_board = { config = {P1,0,0,0,0,0,0,0,0}, children = {} }
local function checkIfOver( board, step )
local config = board.config
local over = false
--check rows
for i=0,ROW-1 do
over = true
for j=1,COL do
if 0 == config[i*COL+1] or config[i*COL+j] ~= config[i*COL+1] then
over = false
end
end
if over then
return config[i*COL+1]
end
end
--check cols
for i=1,COL do
over = true
for j=0,ROW-1 do
if 0 == config[i] or config[i] ~= config[i+COL*j] then
over = false
end
end
if over then
return config[i]
end
end
--check diagonals
if config[1] ~= 0 and config[1] == config[5] and config[5] == config[9] then
return config[1]
end
if config[3] ~=0 and config[3] == config[5] and config[5] == config[7] then
return config[3]
end
if step >= MAX_STEP then
return TIE
else
return ING
end
end
local function treeBuilder( board, step )
--check the game is over
local over = checkIfOver( board, step )
if over ~= ING then
printBoard( board )
print( STATUS[over], '\n---------\n' )
return
end
local child
local childCfg
for i,v in ipairs(board.config) do
if 0 == v then
child = { config = {}, children = {} }
childCfg = shallowCopy( board.config )
childCfg[i] = (step % 2 == 0) and P1 or P2
child.config = childCfg
table.insert( board.children, child )
treeBuilder( child, step + 1 )
end
end
end
treeBuilder( start_board, 1 )