ML Practical No 01
October 27, 2025
Name : Ketaki Ketan Pawar
Roll. No : 16
Batch : AI&DS
.
Title : Apply LDA Algorithm on Iris Dataset and classify which species a given flower belongs to.
Dataset Link:[Link]
[1]: import pandas as pd
from sklearn.model_selection import train_test_split
from [Link] import LabelEncoder
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from [Link] import accuracy_score, classification_report,␣
↪confusion_matrix
data = pd.read_csv("[Link]")
[2]: print("First 5 rows of dataset:\n", [Link]())
print("\nDataset Info:\n")
print([Link]())
First 5 rows of dataset:
Id SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm Species
0 1 5.1 3.5 1.4 0.2 Iris-setosa
1 2 4.9 3.0 1.4 0.2 Iris-setosa
2 3 4.7 3.2 1.3 0.2 Iris-setosa
3 4 4.6 3.1 1.5 0.2 Iris-setosa
4 5 5.0 3.6 1.4 0.2 Iris-setosa
Dataset Info:
<class '[Link]'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Id 150 non-null int64
1
1 SepalLengthCm 150 non-null float64
2 SepalWidthCm 150 non-null float64
3 PetalLengthCm 150 non-null float64
4 PetalWidthCm 150 non-null float64
5 Species 150 non-null object
dtypes: float64(4), int64(1), object(1)
memory usage: 7.2+ KB
None
[3]: X = data[['SepalLengthCm', 'SepalWidthCm', 'PetalLengthCm', 'PetalWidthCm']]
y = data['Species']
le = LabelEncoder()
y_encoded = le.fit_transform(y)
X_train, X_test, y_train, y_test = train_test_split(X, y_encoded, test_size=0.
↪2, random_state=42)
lda = LinearDiscriminantAnalysis()
[Link](X_train, y_train)
y_pred = [Link](X_test)
print("\nAccuracy:", accuracy_score(y_test, y_pred))
print("\nClassification Report:\n", classification_report(y_test, y_pred,␣
↪target_names=le.classes_))
print("\nConfusion Matrix:\n", confusion_matrix(y_test, y_pred))
sample_flower = [[5.2, 3.4, 1.5, 0.2]]
predicted_class = [Link](sample_flower)
predicted_species = le.inverse_transform(predicted_class)
print("\nThe given flower belongs to species:", predicted_species[0])
Accuracy: 1.0
Classification Report:
precision recall f1-score support
Iris-setosa 1.00 1.00 1.00 10
Iris-versicolor 1.00 1.00 1.00 9
Iris-virginica 1.00 1.00 1.00 11
accuracy 1.00 30
macro avg 1.00 1.00 1.00 30
weighted avg 1.00 1.00 1.00 30
2
Confusion Matrix:
[[10 0 0]
[ 0 9 0]
[ 0 0 11]]
The given flower belongs to species: Iris-setosa
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\utils\[Link]:
UserWarning: X does not have valid feature names, but LinearDiscriminantAnalysis
was fitted with feature names
[Link](
[ ]:
3
ML Practical No 02
October 27, 2025
Name : Ketaki Ketan Pawar
Roll. No : 16
Batch : AI&DS
.
Title : Predict the price of the Uber ride from a given pickup point to the agreed drop-off location.
Perform following tasks:
1. Pre-process the dataset.
2. Identify outliers.
3. Check the correlation.
4. Implement linear regression and ridge, Lasso regression models.
5. Evaluate the models and compare their respective scores like R2, RMSE, etc.
Dataset link: [Link]
[19]: import pandas as pd
import numpy as np
import [Link] as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from [Link] import StandardScaler
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from [Link] import mean_squared_error, r2_score
df = "[Link]"
data = pd.read_csv(df)
[20]: data = [Link](['Unnamed: 0', 'key'], axis=1)
data['pickup_datetime'] = pd.to_datetime(data['pickup_datetime'],␣
↪errors='coerce')
data = [Link](subset=['pickup_datetime']) # drop invalid datetime rows
data['hour'] = data['pickup_datetime'].[Link]
data['day'] = data['pickup_datetime'].[Link]
data['month'] = data['pickup_datetime'].[Link]
data['year'] = data['pickup_datetime'].[Link]
data = [Link](['pickup_datetime'], axis=1)
1
data = data[(data['fare_amount'] > 0) & (data['fare_amount'] < 500)]
data = data[(data['passenger_count'] > 0) & (data['passenger_count'] <= 6)]
data = data[(data['pickup_longitude'].between(-75, -72)) &
(data['pickup_latitude'].between(40, 42))]
data = data[(data['dropoff_longitude'].between(-75, -72)) &
(data['dropoff_latitude'].between(40, 42))]
def haversine_distance(lat1, lon1, lat2, lon2):
R = 6371 # radius of Earth in km
phi1 = [Link](lat1)
phi2 = [Link](lat2)
delta_phi = [Link](lat2 - lat1)
delta_lambda = [Link](lon2 - lon1)
a = [Link](delta_phi/2)**2 + [Link](phi1)*[Link](phi2)*[Link](delta_lambda/
↪2)**2
c = 2 * np.arctan2([Link](a), [Link](1-a))
return R * c
data['distance_km'] = haversine_distance(
data['pickup_latitude'], data['pickup_longitude'],
data['dropoff_latitude'], data['dropoff_longitude']
)
data = [Link](['pickup_latitude', 'pickup_longitude', 'dropoff_latitude',␣
↪'dropoff_longitude'], axis=1)
[21]: [Link](figsize=(10,5))
[Link](data['fare_amount'])
[Link]('Fare Amount Outliers')
[Link]()
2
[22]: [Link](figsize=(10,5))
[Link](data['distance_km'])
[Link]('Distance Outliers')
[Link]()
[23]: [Link](figsize=(10,6))
[Link]([Link](), annot=True, cmap='coolwarm')
[Link]('Correlation Matrix')
[Link]()
3
[24]: X = [Link]('fare_amount', axis=1)
y = data['fare_amount']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,␣
↪random_state=42)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = [Link](X_test)
[27]: lr = LinearRegression()
[Link](X_train_scaled, y_train)
y_pred_lr = [Link](X_test_scaled)
ridge = Ridge(alpha=1.0)
[Link](X_train_scaled, y_train)
y_pred_ridge = [Link](X_test_scaled)
lasso = Lasso(alpha=0.1)
[Link](X_train_scaled, y_train)
y_pred_lasso = [Link](X_test_scaled)
4
def evaluate(y_test, y_pred, model_name):
rmse = [Link](mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
print(f"{model_name} -> RMSE: {rmse:.2f}, R2: {r2:.2f}")
evaluate(y_test, y_pred_lr, "Linear Regression")
evaluate(y_test, y_pred_ridge, "Ridge Regression")
evaluate(y_test, y_pred_lasso, "Lasso Regression")
results = [Link]({
'Model': ['Linear Regression', 'Ridge Regression', 'Lasso Regression'],
'RMSE': [
[Link](mean_squared_error(y_test, y_pred_lr)),
[Link](mean_squared_error(y_test, y_pred_ridge)),
[Link](mean_squared_error(y_test, y_pred_lasso))
],
'R2': [
r2_score(y_test, y_pred_lr),
r2_score(y_test, y_pred_ridge),
r2_score(y_test, y_pred_lasso)
]
})
print("\nModel Comparison:\n", results)
Linear Regression -> RMSE: 5.44, R2: 0.69
Ridge Regression -> RMSE: 5.44, R2: 0.69
Lasso Regression -> RMSE: 5.44, R2: 0.69
Model Comparison:
Model RMSE R2
0 Linear Regression 5.435369 0.689765
1 Ridge Regression 5.435370 0.689765
2 Lasso Regression 5.439011 0.689349
[ ]:
5
ML Practical No 03
October 27, 2025
Name : Ketaki Ketan Pawar
Roll. No : 16
Batch : AI&DS
.
Title : Implementation of Support Vector Machines (SVM) for classifying images of hand written
digits into their respective numerical classes (0 to 9).
[8]: import numpy as np
import [Link] as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
from [Link] import StandardScaler
from [Link] import SVC
from [Link] import confusion_matrix, classification_report,␣
↪accuracy_score
digits = datasets.load_digits()
X = [Link]
y = [Link]
print("Shape of data:", [Link])
print("Shape of labels:", [Link])
Shape of data: (1797, 64)
Shape of labels: (1797,)
[9]: [Link](figsize=(10,4))
for i in range(8):
[Link](2,4,i+1)
[Link]([Link][i], cmap='gray')
[Link](f"Label: {[Link][i]}")
[Link]('off')
[Link]()
1
[10]: X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = [Link](X_test)
svm_model = SVC(kernel='rbf', gamma='scale', C=10)
svm_model.fit(X_train_scaled, y_train)
y_pred = svm_model.predict(X_test_scaled)
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy*100:.2f}%\n")
cm = confusion_matrix(y_test, y_pred)
print("Confusion Matrix:\n", cm, "\n")
print("Classification Report:\n", classification_report(y_test, y_pred))
Accuracy: 98.06%
Confusion Matrix:
[[33 0 0 0 0 0 0 0 0 0]
[ 0 28 0 0 0 0 0 0 0 0]
[ 0 0 33 0 0 0 0 0 0 0]
[ 0 0 0 33 0 1 0 0 0 0]
[ 0 0 0 0 46 0 0 0 0 0]
[ 0 0 0 0 0 46 1 0 0 0]
2
[ 0 0 0 0 0 0 35 0 0 0]
[ 0 0 0 0 1 0 0 32 0 1]
[ 0 0 1 0 0 0 0 0 29 0]
[ 0 0 0 1 0 0 0 0 1 38]]
Classification Report:
precision recall f1-score support
0 1.00 1.00 1.00 33
1 1.00 1.00 1.00 28
2 0.97 1.00 0.99 33
3 0.97 0.97 0.97 34
4 0.98 1.00 0.99 46
5 0.98 0.98 0.98 47
6 0.97 1.00 0.99 35
7 1.00 0.94 0.97 34
8 0.97 0.97 0.97 30
9 0.97 0.95 0.96 40
accuracy 0.98 360
macro avg 0.98 0.98 0.98 360
weighted avg 0.98 0.98 0.98 360
[11]: [Link](figsize=(10,4))
for i in range(8):
[Link](2,4,i+1)
[Link](X_test[i].reshape(8,8), cmap='gray')
[Link](f"Pred: {y_pred[i]}")
[Link]('off')
[Link]()
3
[ ]:
4
ML Practical No 04
October 27, 2025
Name : Ketaki Ketan Pawar
Roll. No : 16
Batch : AI&DS
.
Title : Implement K-Means clustering on [Link] dataset. Determine the number of clusters using
the elbow method. Dataset Link: [Link]
[17]: import pandas as pd
import [Link] as plt
from [Link] import KMeans
from [Link] import StandardScaler
iris = pd.read_csv("[Link]")
print([Link]())
Id SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm Species
0 1 5.1 3.5 1.4 0.2 Iris-setosa
1 2 4.9 3.0 1.4 0.2 Iris-setosa
2 3 4.7 3.2 1.3 0.2 Iris-setosa
3 4 4.6 3.1 1.5 0.2 Iris-setosa
4 5 5.0 3.6 1.4 0.2 Iris-setosa
[18]: X = [Link][:, :-1].values
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
inertia = []
K = range(1, 11)
for k in K:
kmeans = KMeans(n_clusters=k, random_state=42)
[Link](X_scaled)
[Link](kmeans.inertia_)
[Link](figsize=(8,5))
[Link](K, inertia, 'bo-')
1
[Link]('Number of clusters (k)')
[Link]('Inertia')
[Link]('Elbow Method for Optimal k')
[Link]()
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1419:
UserWarning: KMeans is known to have a memory leak on Windows with MKL, when
there are less chunks than available threads. You can avoid it by setting the
environment variable OMP_NUM_THREADS=1.
[Link](
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1419:
UserWarning: KMeans is known to have a memory leak on Windows with MKL, when
there are less chunks than available threads. You can avoid it by setting the
environment variable OMP_NUM_THREADS=1.
[Link](
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1419:
UserWarning: KMeans is known to have a memory leak on Windows with MKL, when
there are less chunks than available threads. You can avoid it by setting the
environment variable OMP_NUM_THREADS=1.
[Link](
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1419:
UserWarning: KMeans is known to have a memory leak on Windows with MKL, when
there are less chunks than available threads. You can avoid it by setting the
environment variable OMP_NUM_THREADS=1.
[Link](
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1419:
UserWarning: KMeans is known to have a memory leak on Windows with MKL, when
there are less chunks than available threads. You can avoid it by setting the
environment variable OMP_NUM_THREADS=1.
[Link](
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1419:
UserWarning: KMeans is known to have a memory leak on Windows with MKL, when
there are less chunks than available threads. You can avoid it by setting the
environment variable OMP_NUM_THREADS=1.
[Link](
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1419:
UserWarning: KMeans is known to have a memory leak on Windows with MKL, when
there are less chunks than available threads. You can avoid it by setting the
environment variable OMP_NUM_THREADS=1.
[Link](
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1419:
UserWarning: KMeans is known to have a memory leak on Windows with MKL, when
there are less chunks than available threads. You can avoid it by setting the
environment variable OMP_NUM_THREADS=1.
[Link](
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1419:
UserWarning: KMeans is known to have a memory leak on Windows with MKL, when
2
there are less chunks than available threads. You can avoid it by setting the
environment variable OMP_NUM_THREADS=1.
[Link](
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1419:
UserWarning: KMeans is known to have a memory leak on Windows with MKL, when
there are less chunks than available threads. You can avoid it by setting the
environment variable OMP_NUM_THREADS=1.
[Link](
[19]: optimal_k = 3
kmeans = KMeans(n_clusters=optimal_k, random_state=42)
y_kmeans = kmeans.fit_predict(X_scaled)
iris['Cluster'] = y_kmeans
print([Link]())
[Link](figsize=(8,5))
[Link](X_scaled[y_kmeans==0, 0], X_scaled[y_kmeans==0, 1], s=100, c='red',␣
↪label='Cluster 1')
3
[Link](X_scaled[y_kmeans==1, 0], X_scaled[y_kmeans==1, 1], s=100,␣
↪c='blue', label='Cluster 2')
[Link](X_scaled[y_kmeans==2, 0], X_scaled[y_kmeans==2, 1], s=100,␣
↪c='green', label='Cluster 3')
[Link](kmeans.cluster_centers_[:,0], kmeans.cluster_centers_[:,1], s=300,␣
↪c='yellow', label='Centroids')
[Link]('Sepal Length (scaled)')
[Link]('Sepal Width (scaled)')
[Link]('K-Means Clustering on Iris Dataset')
[Link]()
[Link]()
C:\Users\VINU\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1419:
UserWarning: KMeans is known to have a memory leak on Windows with MKL, when
there are less chunks than available threads. You can avoid it by setting the
environment variable OMP_NUM_THREADS=1.
[Link](
Id SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm Species \
0 1 5.1 3.5 1.4 0.2 Iris-setosa
1 2 4.9 3.0 1.4 0.2 Iris-setosa
2 3 4.7 3.2 1.3 0.2 Iris-setosa
3 4 4.6 3.1 1.5 0.2 Iris-setosa
4 5 5.0 3.6 1.4 0.2 Iris-setosa
Cluster
0 2
1 2
2 2
3 2
4 2
4
[ ]:
5
ML Practical No 05
October 27, 2025
Name : Ketaki Ketan Pawar
Roll. No : 16
Batch : AI&DS
.
Title : Implement Random Forest Classifier model to predict the safety of the car. Dataset link:
[Link]
[11]: import pandas as pd
from sklearn.model_selection import train_test_split
from [Link] import LabelEncoder
from [Link] import RandomForestClassifier
from [Link] import accuracy_score, confusion_matrix,␣
↪classification_report
import [Link] as plt
columns = ['buying', 'maint', 'doors', 'persons', 'lug_boot', 'safety', 'class']
data = pd.read_csv('car_evaluation.csv', names=columns, header=None)
print([Link]())
buying maint doors persons lug_boot safety class
0 vhigh vhigh 2 2 small low unacc
1 vhigh vhigh 2 2 small med unacc
2 vhigh vhigh 2 2 small high unacc
3 vhigh vhigh 2 2 med low unacc
4 vhigh vhigh 2 2 med med unacc
[12]: le = LabelEncoder()
for col in [Link]:
data[col] = le.fit_transform(data[col])
X = [Link]('class', axis=1)
y = data['class']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,␣
↪random_state=42)
1
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
rf_model.fit(X_train, y_train)
y_pred = rf_model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy*100:.2f}%\n")
print("Confusion Matrix:\n", confusion_matrix(y_test, y_pred))
print("\nClassification Report:\n", classification_report(y_test, y_pred))
features = [Link]
importances = rf_model.feature_importances_
indices = [Link]()[::-1]
Accuracy: 97.40%
Confusion Matrix:
[[ 75 6 2 0]
[ 0 11 0 0]
[ 0 0 235 0]
[ 1 0 0 16]]
Classification Report:
precision recall f1-score support
0 0.99 0.90 0.94 83
1 0.65 1.00 0.79 11
2 0.99 1.00 1.00 235
3 1.00 0.94 0.97 17
accuracy 0.97 346
macro avg 0.91 0.96 0.92 346
weighted avg 0.98 0.97 0.98 346
[13]: [Link](figsize=(8,5))
[Link](range([Link][1]), importances[indices], color='skyblue',␣
↪align='center')
[Link](range([Link][1]), [features[i] for i in indices], rotation=45)
[Link]("Feature Importance")
[Link]()
2
[ ]:
3
ML Practical No 06
October 27, 2025
Name : Ketaki Ketan Pawar
Roll. No : 16
Batch : AI&DS
.
Title : Build a Tic-Tac-Toe game using reinforcement learning in Python by using following tasks
a. Setting up the environment b. Defining the Tic-Tac-Toe game c. Building the reinforcement
learning model d. Training the model e. Testing the model
[11]: import numpy as np
import random
class TicTacToe:
def __init__(self):
[Link] = [' ']*9
self.current_winner = None
def reset(self):
[Link] = [' ']*9
self.current_winner = None
return self.get_state()
def get_state(self):
return ''.join([Link])
def available_actions(self):
return [i for i, spot in enumerate([Link]) if spot == ' ']
def make_move(self, square, letter):
if [Link][square] == ' ':
[Link][square] = letter
if [Link](square, letter):
self.current_winner = letter
return True
return False
def winner(self, square, letter):
1
# Check rows, columns, diagonals
row_ind = square // 3
if all([[Link][row_ind*3 + i] == letter for i in range(3)]):
return True
col_ind = square % 3
if all([[Link][col_ind + 3*i] == letter for i in range(3)]):
return True
if square % 2 == 0:
if all([[Link][i] == letter for i in [0,4,8]]) or all([self.
↪board[i] == letter for i in [2,4,6]]):
return True
return False
def is_done(self):
return self.current_winner is not None or ' ' not in [Link]
def render(self):
print()
for i in range(3):
print([Link][i*3:(i+1)*3])
print()
[12]: class QLearningAgent:
def __init__(self, alpha=0.3, gamma=0.9, epsilon=0.2):
self.q_table = {}
[Link] = alpha
[Link] = gamma
[Link] = epsilon
def get_q(self, state, action):
return self.q_table.get((state, action), 0.0)
def choose_action(self, state, available_actions):
# Epsilon-greedy policy
if [Link]() < [Link]:
return [Link](available_actions)
qs = [self.get_q(state, a) for a in available_actions]
max_q = max(qs)
max_actions = [a for a, q in zip(available_actions, qs) if q == max_q]
return [Link](max_actions)
def learn(self, state, action, reward, next_state, next_available_actions,␣
↪done):
current_q = self.get_q(state, action)
if done:
target = reward
else:
2
next_qs = [self.get_q(next_state, a) for a in␣
↪next_available_actions]
target = reward + [Link] * max(next_qs)
self.q_table[(state, action)] = current_q + [Link] * (target -␣
↪current_q)
[13]: def train(agent, episodes=50000):
for episode in range(episodes):
game = TicTacToe()
state = [Link]()
done = False
while not done:
available = game.available_actions()
action = agent.choose_action(state, available)
game.make_move(action, 'X')
if game.current_winner == 'X':
[Link](state, action, reward=1, next_state=None,␣
↪next_available_actions=[], done=True)
break
elif game.is_done():
[Link](state, action, reward=0.5, next_state=None,␣
↪next_available_actions=[], done=True)
break
# Opponent random move
opp_available = game.available_actions()
opp_action = [Link](opp_available)
game.make_move(opp_action, 'O')
next_state = game.get_state()
next_available = game.available_actions()
if game.current_winner == 'O':
[Link](state, action, reward=-1, next_state=next_state,␣
↪next_available_actions=next_available, done=True)
break
elif game.is_done():
[Link](state, action, reward=0.5, next_state=next_state,␣
↪next_available_actions=next_available, done=True)
break
else:
[Link](state, action, reward=0, next_state=next_state,␣
↪next_available_actions=next_available, done=False)
state = next_state
3
[14]: def play(agent):
game = TicTacToe()
state = [Link]()
done = False
[Link]()
while not done:
available = game.available_actions()
action = agent.choose_action(state, available)
game.make_move(action, 'X')
print("Agent's Move:")
[Link]()
if game.current_winner == 'X':
print("Agent Wins!")
break
elif game.is_done():
print("Draw!")
break
# Opponent move
opp_available = game.available_actions()
opp_action = int(input(f"Your move {opp_available}: "))
while opp_action not in opp_available:
opp_action = int(input(f"Invalid! Choose from {opp_available}: "))
game.make_move(opp_action, 'O')
[Link]()
if game.current_winner == 'O':
print("You Win!")
break
state = game.get_state()
[15]: agent = QLearningAgent(alpha=0.3, gamma=0.9, epsilon=0.2)
train(agent, episodes=50000)
play(agent)
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
Agent's Move:
[' ', ' ', 'X']
[' ', ' ', ' ']
[' ', ' ', ' ']
Your move [0, 1, 3, 4, 5, 6, 7, 8]: 4
4
[' ', ' ', 'X']
[' ', 'O', ' ']
[' ', ' ', ' ']
Agent's Move:
[' ', ' ', 'X']
[' ', 'O', 'X']
[' ', ' ', ' ']
Your move [0, 1, 3, 6, 7, 8]: 8
[' ', ' ', 'X']
[' ', 'O', 'X']
[' ', ' ', 'O']
Agent's Move:
['X', ' ', 'X']
[' ', 'O', 'X']
[' ', ' ', 'O']
Your move [1, 3, 6, 7]: 1
['X', 'O', 'X']
[' ', 'O', 'X']
[' ', ' ', 'O']
Agent's Move:
['X', 'O', 'X']
[' ', 'O', 'X']
[' ', 'X', 'O']
Your move [3, 6]: 6
['X', 'O', 'X']
[' ', 'O', 'X']
['O', 'X', 'O']
Agent's Move:
['X', 'O', 'X']
['X', 'O', 'X']
5
['O', 'X', 'O']
Draw!
[ ]: