summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--index.html3
-rw-r--r--projects_blog/Tic-Tac-Toe-ML-Lab-Kit.html75
-rw-r--r--projects_blog/presentation_code/tictactoe.md175
-rw-r--r--projects_blog/presentation_code/tictactoe.py174
-rw-r--r--style.css5
5 files changed, 431 insertions, 1 deletions
diff --git a/index.html b/index.html
index 142fab3..9d756ee 100644
--- a/index.html
+++ b/index.html
@@ -25,8 +25,9 @@
</ul>
<ul>
- Projects
+ Personal Projects
<li class="nnetwork"><a href="projects_blog/NNetwork.html">NNetwork</a></li>
+ <!-- <li class="tic-tac-toe"><a href="projects_blog/Tic-Tac-Toe-ML-Lab-Kit.html">Tic Tac Toe Machine Learning Lab Kit</a></li> -->
</ul>
<nav class="nav-footer">
diff --git a/projects_blog/Tic-Tac-Toe-ML-Lab-Kit.html b/projects_blog/Tic-Tac-Toe-ML-Lab-Kit.html
new file mode 100644
index 0000000..7780765
--- /dev/null
+++ b/projects_blog/Tic-Tac-Toe-ML-Lab-Kit.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>chrhodgden - NNetwork</title>
+ <link rel="stylesheet" href="../style.css"/>
+ <style>
+ :root {
+ --theme-color-check: 0;
+ --accent-hue: var(--tic-tac-toe-hue);
+ }
+ </style>
+</head>
+<body>
+
+ <div class="dark-mode-container">
+ <input type="checkbox" id="--dark-theme-check">Dark Mode</input>
+ </div>
+
+ <h1>Tic Tac Toe Machine Learning Lab Kit</h1>
+ <p class="header-sub-text">An Interactive Game in which you Train an AI to Play Tic Tac Toe</p>
+ <nav class="nav-footer">
+ <hr>
+ <a href="#background">Background</a>
+ | <a href="#the-project">The Project</a>
+ | <a href="#next-steps">Next Steps</a>
+ <hr>
+ </nav>
+ <br>
+ <a href="https://github.com/chrhodgden/Tic-Tac-Toe-ML-Lab-Kit" target="_blank">GitHub Repository</a>
+ <h2 id="background">Background</h2>
+ <p>
+ As I was learning python, I made a very simple inteactive tic tac toe game.
+ I kept polishing it instead of progressing through the tutorial I was in.
+ In pushing myself to add and debug features I found that I got to a point where I could teach myself by referencing Python's own documentation.
+ </p>
+ <a href="./presentation_code/tictactoe.md" target="_blank">Simple version</a>
+ <p>
+ This version will work on all platformas that run Python.
+ </p>
+ <p>
+ When I got this version complete, I continued with other tutorials to learn about object oriented programming and other Python libraries.
+ </p>
+ <h2 id="the-project">The Project</h2>
+ <p>
+ This was a simple and fun project for me to learn python.
+ Not all the menu items have been set up and will crash if they are selected.
+ The keyboard interface only works with Microsoft Windows OS.
+ </p>
+ <p>
+ It does not apply proper machine learning techniques.
+ The "machine learning" is aggregating in raw SQL for each occurance.
+ They do infact improve and figure out how to play.
+ </p>
+
+ <h2 id="next-steps">Next Steps</h2>
+ <p>
+ Next steps for this project will be to integrate proper machine learning into the the AI profiles.
+ Then add the rest of the menu elements.
+ </p>
+ <p>
+ I may decide to do this in JavaScript and HTML with the Math.js or TensorFlow libraries.
+ The potential for this project is educational.
+ It would be nice to share easily and be more interactive.
+ </p>
+
+ <nav class="nav-footer">
+ <hr>
+ <a href="..\index.html">Home Page</a>
+ | <a href="..\about.html">About Page</a>
+ | <a href="#">Top of Page</a>
+ <hr>
+ </nav>
+ <script src="../app.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/projects_blog/presentation_code/tictactoe.md b/projects_blog/presentation_code/tictactoe.md
new file mode 100644
index 0000000..dab8c4f
--- /dev/null
+++ b/projects_blog/presentation_code/tictactoe.md
@@ -0,0 +1,175 @@
+```python
+#Next Steps:
+# Build & export move log for games
+# Build a board timeline variable/class - Export this?
+# Build an analysys board class for bots to use thinking through the next commands.
+# Look into usin scikit-learn to create a decistion tree model, then have a bot level that loads and uses the model. - See if you could have it programmed itself playing against the other bot levels. Probably need an X model & O model.
+# Probably need to translate some of the variables as classes -- THIS WILL BE GOOD FOR EDUCATION -- I NEED TO PRACTICE OBJECT-ORIENTED PROGRAMMING AT LEAST ONCE YOU'D THINK.
+# Probably should export the bot functionality to a separate module
+
+
+import random
+
+def prt_brd(brd):
+ print(f"""
+3 {brd[0][2]}|{brd[1][2]}|{brd[2][2]}
+2 {brd[0][1]}|{brd[1][1]}|{brd[2][1]}
+1 {brd[0][0]}|{brd[1][0]}|{brd[2][0]}
+ a b c
+""")
+
+def rnd(i = 1): return random.randint(0,i)
+
+def bot_cmd(lvl, trn, brd):
+ if lvl >= 1:
+ #check for potential victory
+ #check files and ranks
+ for r in range(0,3):
+ if brd[r].count(trn) == 2 and brd[r].count(' ') == 1: return file_name[r]+rank_name[brd[r].index(' ')]
+ if (brd[0][r]+brd[1][r]+brd[2][r]).count(trn) == 2 and (brd[0][r]+brd[1][r]+brd[2][r]).count(' ') == 1: return file_name[(brd[0][r]+brd[1][r]+brd[2][r]).index(' ')]+rank_name[r]
+
+ #check diagonals
+ if (brd[0][0]+brd[1][1]+brd[2][2]).count(trn) == 2 and (brd[0][0]+brd[1][1]+brd[2][2]).count(' ') == 1: return file_name[(brd[0][0]+brd[1][1]+brd[2][2]).index(' ')]+rank_name[(brd[0][0]+brd[1][1]+brd[2][2]).index(' ')]
+ if (brd[0][2]+brd[1][1]+brd[2][0]).count(trn) == 2 and (brd[0][2]+brd[1][1]+brd[2][0]).count(' ') == 1: return file_name[(brd[0][2]+brd[1][1]+brd[2][0]).index(' ')]+rank_name[(brd[2][0]+brd[1][1]+brd[0][2]).index(' ')]
+
+ if lvl >= 2:
+ alt_trn = plr_trn[trn == 'X']
+ #check for potential next-turn loss
+ #check files and ranks
+ for r in range(0,3):
+ if brd[r].count(alt_trn) == 2 and brd[r].count(' ') == 1: return file_name[r]+rank_name[brd[r].index(' ')]
+ if (brd[0][r]+brd[1][r]+brd[2][r]).count(alt_trn) == 2 and (brd[0][r]+brd[1][r]+brd[2][r]).count(' ') == 1: return file_name[(brd[0][r]+brd[1][r]+brd[2][r]).index(' ')]+rank_name[r]
+
+ #check diagonals
+ if (brd[0][0]+brd[1][1]+brd[2][2]).count(alt_trn) == 2 and (brd[0][0]+brd[1][1]+brd[2][2]).count(' ') == 1: return file_name[(brd[0][0]+brd[1][1]+brd[2][2]).index(' ')]+rank_name[(brd[0][0]+brd[1][1]+brd[2][2]).index(' ')]
+ if (brd[0][2]+brd[1][1]+brd[2][0]).count(alt_trn) == 2 and (brd[0][2]+brd[1][1]+brd[2][0]).count(' ') == 1: return file_name[(brd[0][2]+brd[1][1]+brd[2][0]).index(' ')]+rank_name[(brd[2][0]+brd[1][1]+brd[0][2]).index(' ')]
+
+ if lvl >= 3:
+ random.shuffle(vic_rng)
+ rng_ind = [0, 1, 2]
+ #build on existing rows
+ for rng in vic_rng:
+ for d in range(0,3):
+ if rng == file_name[d] and brd[d].count(trn) == 1 and brd[d].count(' ') == 2:
+ rng_ind.remove(brd[d].index(trn))
+ return file_name[d]+rank_name[rng_ind[rnd()]]
+ elif rng == rank_name[d] and (brd[0][d]+brd[1][d]+brd[2][d]).count(trn) == 1 and (brd[0][d]+brd[1][d]+brd[2][d]).count(' ') == 2:
+ rng_ind.remove((brd[0][d]+brd[1][d]+brd[2][d]).index(trn))
+ return file_name[rng_ind[rnd()]]+rank_name[d]
+ if rng == 'd1' and (brd[0][0]+brd[1][1]+brd[2][2]).count(trn) == 1 and (brd[0][0]+brd[1][1]+brd[2][2]).count(' ') == 2:
+ rng_ind.remove((brd[0][0]+brd[1][1]+brd[2][2]).index(trn))
+ f = rng_ind[rnd()]
+ return file_name[f]+rank_name[f]
+ elif rng == 'd2' and (brd[0][2]+brd[1][1]+brd[2][0]).count(trn) == 1 and (brd[0][2]+brd[1][1]+brd[2][0]).count(' ') == 2:
+ rng_ind.remove((brd[0][2]+brd[1][1]+brd[2][0]).index(trn))
+ f = rng_ind[rnd()]
+ return file_name[f]+rank_name[2 - f]
+
+ while True:
+ f = rnd(2)
+ r = rnd(2)
+ if board[f][r] == ' ': return file_name[f]+rank_name[r]
+
+rank = [' ', ' ', ' ']
+file = [' ', ' ', ' ']
+file_name = ['a', 'b', 'c']
+rank_name = ['1', '2', '3']
+f = 0
+r = 0
+d = 0
+turn = 'X'
+vic_chk = False
+valid_move = False
+board = [file[:], file[:], file[:]]
+plrs = -1
+plrs_str = '0'
+plr = [False, False]
+plr_trn = ('X', 'O')
+one_plr = 'X'
+bot_lvl = [-1, -1]
+lvl_str = ''
+bot_lvl_name = ['Random', 'Easy', 'Medium', 'Medium_2']
+vic_rng = file_name + rank_name + ['d1', 'd2']
+rng = ''
+rng_ind = [0, 1, 2]
+
+input("""----- TIC TAC TOE -----
+Press Enter to play!
+""")
+
+while plrs > 2 or plrs < 0:
+ plrs_str = input("Enter the number of players: ")
+ if len(plrs_str) > 0:
+ if plrs_str[0].isnumeric(): plrs = int(plrs_str[0])
+print()
+
+for r in range(0, plrs):
+ plr[r] = True
+
+if plrs == 1:
+ while True:
+ one_plr = input("Do you want play as the X's or as the O's? ").upper()
+ if len(one_plr) > 0:
+ if one_plr[0] == 'X' or one_plr[0] == 'O':
+ plr = [one_plr[0] == 'X', one_plr[0] == 'O']
+ print()
+ break
+
+if False in plr:
+ for r in range(0, len(bot_lvl_name)):
+ print(f"Level {r} - {bot_lvl_name[r]}")
+ print()
+
+for r in range(0,2):
+ while not plr[r] and bot_lvl[r] == -1:
+ lvl_str = input(f"Please select {plr_trn[r]} bot level number: ")
+ if len(lvl_str) > 0:
+ if lvl_str[0].isnumeric(): bot_lvl[r] = int(lvl_str[0])
+
+prt_brd(board)
+
+while True:
+
+ valid_move = False
+ while not valid_move:
+ #player uses input()
+ #computer uses bot_cmd()
+ if plr[turn == 'O']: cmd = input(f"{turn} to Move: ")
+ else:
+ cmd = bot_cmd(bot_lvl[turn == 'O'], turn, board)
+ print(f"{turn} to Move: {cmd}")
+ if len(cmd) == 2:
+ if file_name.count(cmd[0]) == 1 and rank_name.count(cmd[1]) == 1:
+ f = file_name.index(cmd[0])
+ r = rank_name.index(cmd[1])
+ if board[f][r] == ' ':
+ board[f][r] = turn
+ valid_move = True
+ else: print("Illegal move")
+ else: print("Invalid move")
+ else: print("Invalid move")
+
+ prt_brd(board)
+
+ #check for victory
+ #check files and ranks
+ for r in range(0,3):
+ if board[r].count(turn) == 3: vic_chk = True
+ if (board[0][r]+board[1][r]+board[2][r]).count(turn) == 3: vic_chk = True
+
+ #check diagonals
+ if (board[0][0]+board[1][1]+board[2][2]).count(turn) == 3: vic_chk = True
+ if (board[0][2]+board[1][1]+board[2][0]).count(turn) == 3: vic_chk = True
+
+ if vic_chk: break
+ if board[0].count(' ') + board[1].count(' ') + board[2].count(' ') == 0: break
+
+ turn = plr_trn[turn == 'X']
+
+if vic_chk: print(f"{turn} Wins!")
+else: print("Draw")
+
+input()
+input("""----- Thank you for playing! -----
+Press Enter to exit.
+""") \ No newline at end of file
diff --git a/projects_blog/presentation_code/tictactoe.py b/projects_blog/presentation_code/tictactoe.py
new file mode 100644
index 0000000..cad2a2f
--- /dev/null
+++ b/projects_blog/presentation_code/tictactoe.py
@@ -0,0 +1,174 @@
+#Next Steps:
+# Build & export move log for games
+# Build a board timeline variable/class - Export this?
+# Build an analysys board class for bots to use thinking through the next commands.
+# Look into usin scikit-learn to create a decistion tree model, then have a bot level that loads and uses the model. - See if you could have it programmed itself playing against the other bot levels. Probably need an X model & O model.
+# Probably need to translate some of the variables as classes -- THIS WILL BE GOOD FOR EDUCATION -- I NEED TO PRACTICE OBJECT-ORIENTED PROGRAMMING AT LEAST ONCE YOU'D THINK.
+# Probably should export the bot functionality to a separate module
+
+
+import random
+
+def prt_brd(brd):
+ print(f"""
+3 {brd[0][2]}|{brd[1][2]}|{brd[2][2]}
+2 {brd[0][1]}|{brd[1][1]}|{brd[2][1]}
+1 {brd[0][0]}|{brd[1][0]}|{brd[2][0]}
+ a b c
+""")
+
+def rnd(i = 1): return random.randint(0,i)
+
+def bot_cmd(lvl, trn, brd):
+ if lvl >= 1:
+ #check for potential victory
+ #check files and ranks
+ for r in range(0,3):
+ if brd[r].count(trn) == 2 and brd[r].count(' ') == 1: return file_name[r]+rank_name[brd[r].index(' ')]
+ if (brd[0][r]+brd[1][r]+brd[2][r]).count(trn) == 2 and (brd[0][r]+brd[1][r]+brd[2][r]).count(' ') == 1: return file_name[(brd[0][r]+brd[1][r]+brd[2][r]).index(' ')]+rank_name[r]
+
+ #check diagonals
+ if (brd[0][0]+brd[1][1]+brd[2][2]).count(trn) == 2 and (brd[0][0]+brd[1][1]+brd[2][2]).count(' ') == 1: return file_name[(brd[0][0]+brd[1][1]+brd[2][2]).index(' ')]+rank_name[(brd[0][0]+brd[1][1]+brd[2][2]).index(' ')]
+ if (brd[0][2]+brd[1][1]+brd[2][0]).count(trn) == 2 and (brd[0][2]+brd[1][1]+brd[2][0]).count(' ') == 1: return file_name[(brd[0][2]+brd[1][1]+brd[2][0]).index(' ')]+rank_name[(brd[2][0]+brd[1][1]+brd[0][2]).index(' ')]
+
+ if lvl >= 2:
+ alt_trn = plr_trn[trn == 'X']
+ #check for potential next-turn loss
+ #check files and ranks
+ for r in range(0,3):
+ if brd[r].count(alt_trn) == 2 and brd[r].count(' ') == 1: return file_name[r]+rank_name[brd[r].index(' ')]
+ if (brd[0][r]+brd[1][r]+brd[2][r]).count(alt_trn) == 2 and (brd[0][r]+brd[1][r]+brd[2][r]).count(' ') == 1: return file_name[(brd[0][r]+brd[1][r]+brd[2][r]).index(' ')]+rank_name[r]
+
+ #check diagonals
+ if (brd[0][0]+brd[1][1]+brd[2][2]).count(alt_trn) == 2 and (brd[0][0]+brd[1][1]+brd[2][2]).count(' ') == 1: return file_name[(brd[0][0]+brd[1][1]+brd[2][2]).index(' ')]+rank_name[(brd[0][0]+brd[1][1]+brd[2][2]).index(' ')]
+ if (brd[0][2]+brd[1][1]+brd[2][0]).count(alt_trn) == 2 and (brd[0][2]+brd[1][1]+brd[2][0]).count(' ') == 1: return file_name[(brd[0][2]+brd[1][1]+brd[2][0]).index(' ')]+rank_name[(brd[2][0]+brd[1][1]+brd[0][2]).index(' ')]
+
+ if lvl >= 3:
+ random.shuffle(vic_rng)
+ rng_ind = [0, 1, 2]
+ #build on existing rows
+ for rng in vic_rng:
+ for d in range(0,3):
+ if rng == file_name[d] and brd[d].count(trn) == 1 and brd[d].count(' ') == 2:
+ rng_ind.remove(brd[d].index(trn))
+ return file_name[d]+rank_name[rng_ind[rnd()]]
+ elif rng == rank_name[d] and (brd[0][d]+brd[1][d]+brd[2][d]).count(trn) == 1 and (brd[0][d]+brd[1][d]+brd[2][d]).count(' ') == 2:
+ rng_ind.remove((brd[0][d]+brd[1][d]+brd[2][d]).index(trn))
+ return file_name[rng_ind[rnd()]]+rank_name[d]
+ if rng == 'd1' and (brd[0][0]+brd[1][1]+brd[2][2]).count(trn) == 1 and (brd[0][0]+brd[1][1]+brd[2][2]).count(' ') == 2:
+ rng_ind.remove((brd[0][0]+brd[1][1]+brd[2][2]).index(trn))
+ f = rng_ind[rnd()]
+ return file_name[f]+rank_name[f]
+ elif rng == 'd2' and (brd[0][2]+brd[1][1]+brd[2][0]).count(trn) == 1 and (brd[0][2]+brd[1][1]+brd[2][0]).count(' ') == 2:
+ rng_ind.remove((brd[0][2]+brd[1][1]+brd[2][0]).index(trn))
+ f = rng_ind[rnd()]
+ return file_name[f]+rank_name[2 - f]
+
+ while True:
+ f = rnd(2)
+ r = rnd(2)
+ if board[f][r] == ' ': return file_name[f]+rank_name[r]
+
+rank = [' ', ' ', ' ']
+file = [' ', ' ', ' ']
+file_name = ['a', 'b', 'c']
+rank_name = ['1', '2', '3']
+f = 0
+r = 0
+d = 0
+turn = 'X'
+vic_chk = False
+valid_move = False
+board = [file[:], file[:], file[:]]
+plrs = -1
+plrs_str = '0'
+plr = [False, False]
+plr_trn = ('X', 'O')
+one_plr = 'X'
+bot_lvl = [-1, -1]
+lvl_str = ''
+bot_lvl_name = ['Random', 'Easy', 'Medium', 'Medium_2']
+vic_rng = file_name + rank_name + ['d1', 'd2']
+rng = ''
+rng_ind = [0, 1, 2]
+
+input("""----- TIC TAC TOE -----
+Press Enter to play!
+""")
+
+while plrs > 2 or plrs < 0:
+ plrs_str = input("Enter the number of players: ")
+ if len(plrs_str) > 0:
+ if plrs_str[0].isnumeric(): plrs = int(plrs_str[0])
+print()
+
+for r in range(0, plrs):
+ plr[r] = True
+
+if plrs == 1:
+ while True:
+ one_plr = input("Do you want play as the X's or as the O's? ").upper()
+ if len(one_plr) > 0:
+ if one_plr[0] == 'X' or one_plr[0] == 'O':
+ plr = [one_plr[0] == 'X', one_plr[0] == 'O']
+ print()
+ break
+
+if False in plr:
+ for r in range(0, len(bot_lvl_name)):
+ print(f"Level {r} - {bot_lvl_name[r]}")
+ print()
+
+for r in range(0,2):
+ while not plr[r] and bot_lvl[r] == -1:
+ lvl_str = input(f"Please select {plr_trn[r]} bot level number: ")
+ if len(lvl_str) > 0:
+ if lvl_str[0].isnumeric(): bot_lvl[r] = int(lvl_str[0])
+
+prt_brd(board)
+
+while True:
+
+ valid_move = False
+ while not valid_move:
+ #player uses input()
+ #computer uses bot_cmd()
+ if plr[turn == 'O']: cmd = input(f"{turn} to Move: ")
+ else:
+ cmd = bot_cmd(bot_lvl[turn == 'O'], turn, board)
+ print(f"{turn} to Move: {cmd}")
+ if len(cmd) == 2:
+ if file_name.count(cmd[0]) == 1 and rank_name.count(cmd[1]) == 1:
+ f = file_name.index(cmd[0])
+ r = rank_name.index(cmd[1])
+ if board[f][r] == ' ':
+ board[f][r] = turn
+ valid_move = True
+ else: print("Illegal move")
+ else: print("Invalid move")
+ else: print("Invalid move")
+
+ prt_brd(board)
+
+ #check for victory
+ #check files and ranks
+ for r in range(0,3):
+ if board[r].count(turn) == 3: vic_chk = True
+ if (board[0][r]+board[1][r]+board[2][r]).count(turn) == 3: vic_chk = True
+
+ #check diagonals
+ if (board[0][0]+board[1][1]+board[2][2]).count(turn) == 3: vic_chk = True
+ if (board[0][2]+board[1][1]+board[2][0]).count(turn) == 3: vic_chk = True
+
+ if vic_chk: break
+ if board[0].count(' ') + board[1].count(' ') + board[2].count(' ') == 0: break
+
+ turn = plr_trn[turn == 'X']
+
+if vic_chk: print(f"{turn} Wins!")
+else: print("Draw")
+
+input()
+input("""----- Thank you for playing! -----
+Press Enter to exit.
+""") \ No newline at end of file
diff --git a/style.css b/style.css
index 0e0e777..ea2eba7 100644
--- a/style.css
+++ b/style.css
@@ -14,6 +14,7 @@
--about-hue: 310;
--nnetwork-hue: 240;
+ --tic-tac-toe-hue: 60;
--foreground-lightness: calc(10% + calc(var(--dark-theme-check) * 70%));
--background-lightness: calc(80% - calc(var(--dark-theme-check) * 70%));
@@ -88,6 +89,10 @@ li.nnetwork {
color: hsl(var(--nnetwork-hue), 100%, var(--foreground-lightness));
}
+li.tic-tac-toe {
+ color: hsl(var(--tic-tac-toe-hue), 100%, var(--foreground-lightness));
+}
+
a {
color: var(--contrast-color);
}