DALL·E 2024-10-01 09.45.46 - A feature image depicting a graph data structure concept. The image should have interconnected nodes (vertices) represented as circles, with edges (li

Search algorithms

In computer science, searching is a fundamental operation, integral to many applications and systems. Whether it’s finding a file on your computer, retrieving a contact from your phone, or even querying data from large databases, search algorithms are behind the scenes ensuring quick and efficient results. This article explores various search algorithms, discussing their types, implementations, and applications in real-world scenarios.

Table of Contents

  1. Introduction to Search Algorithms
  2. Types of Search Algorithms
    • Linear Search
    • Binary Search
    • Hash-based Search
    • Depth First Search (DFS)
    • Breadth First Search (BFS)
  3. Performance Considerations
  4. Applications of Search Algorithms
  5. Conclusion

1. Introduction to Search Algorithms

Search algorithms are a category of algorithms designed to retrieve data from a collection based on certain criteria. The collection could be a list, an array, or a more complex structure like a graph or a database. Efficient search algorithms are critical because they allow computers to quickly retrieve data, even when the data set is massive.

Depending on the nature of the data and its organization, different algorithms can be applied to maximize performance. Search algorithms can be broadly categorized into simple search algorithms (like linear and binary search) and graph traversal algorithms (such as depth-first and breadth-first searches).

Why Search Algorithms Matter?

In the digital world, the size of data sets is continuously increasing, driven by social media, e-commerce platforms, and IoT devices. Efficient search algorithms help reduce computation time, leading to faster response times and better performance. As data expands, it becomes even more important to implement the right search algorithm.


2. Types of Search Algorithms

a. Linear Search

Definition:
Linear search is the simplest form of search algorithm where we scan each element in the list one by one until we find the desired element or reach the end of the list.

 def linear_search(arr, target):
    for i in range(len(arr)):
        if arr[i] == target:
            return i
    return -1

In the above implementation, we search through the list arr looking for the target. If the target is found, the index of that element is returned; otherwise, -1 is returned.

Time Complexity:
The worst-case time complexity of linear search is O(n), where n is the number of elements in the list. It is not suitable for large datasets due to its inefficiency.

When to use:
Linear search is useful when you have unsorted data or when the list is relatively small. It’s often used for simpler or smaller datasets where sorting the data first would introduce unnecessary overhead.

b. Binary Search

Definition:
Binary search is a more efficient algorithm that works on sorted lists. It divides the search interval in half each time, reducing the time complexity significantly compared to linear search.

 def binary_search(arr, target):
    low, high = 0, len(arr) - 1
    while low <= high:
        mid = (low + high) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

Time Complexity:
Binary search has a time complexity of O(log n). It is significantly faster than linear search, especially for large datasets, but it requires the data to be sorted.

When to use:
Binary search is ideal when dealing with sorted data. It is widely used in systems where quick lookups are essential, such as in databases, sorted arrays, and searching in directories.

c. Hash-based Search

Definition:
A hash-based search algorithm uses a hash function to convert a search key into an index within an array or a hash table. This method is commonly used for associative arrays or hash maps.

 def hash_search(data, key):
    return data.get(key, -1)

In the above example, we assume data is a Python dictionary. We use the get method, which searches the dictionary by key and returns the corresponding value.

Time Complexity:
The average time complexity of hash-based search is O(1) for insertions, deletions, and lookups, making it the most efficient for unordered data.

When to use:
Hash-based search is highly effective for searching in hash tables. It is used in database indexing, caching, and finding unique items in collections.

d. Depth First Search (DFS)

Definition:
Depth First Search is a graph traversal algorithm that explores as far down a branch as possible before backtracking. It is often used for tasks like finding connected components in graphs, detecting cycles, and solving mazes.

def dfs(graph, node, visited):
    if node not in visited:
        visited.add(node)
        for neighbor in graph[node]:
            dfs(graph, neighbor, visited)
 

In this implementation, graph is a dictionary where the keys are nodes, and the values are lists of adjacent nodes. We use a set visited to track the nodes that have already been explored.

Time Complexity:
The time complexity of DFS is O(V + E), where V is the number of vertices, and E is the number of edges in the graph.

When to use:
DFS is particularly useful for exploring large graphs, performing exhaustive searches, and solving puzzles or mazes. It’s also used in algorithms like topological sorting and strongly connected components.

e. Breadth First Search (BFS)

Definition:
Breadth First Search is another graph traversal algorithm that explores all neighbors at the present depth before moving to nodes at the next depth level. BFS is commonly used in finding the shortest path in an unweighted graph.

 from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    while queue:
        node = queue.popleft()
        if node not in visited:
            visited.add(node)
            for neighbor in graph[node]:
                queue.append(neighbor)
 

Time Complexity:
Like DFS, the time complexity of BFS is O(V + E). However, BFS explores in layers and is useful for finding the shortest path in unweighted graphs.

When to use:
BFS is ideal for problems requiring level-wise exploration, such as finding the shortest path in unweighted graphs or in situations where you need to explore nodes closer to the starting point before moving deeper.


3. Performance Considerations

When choosing a search algorithm, performance is key. The factors influencing performance include:

  • Data Structure: Different data structures lend themselves to different search algorithms. For example, binary search requires sorted data, whereas hash search requires a hashable data structure.
  • Size of Data: The larger the data set, the more critical it is to choose an efficient algorithm. For instance, linear search performs poorly on large datasets.
  • Time Complexity: Each algorithm has a different time complexity. Algorithms like binary search and hash search are much faster than linear search.
  • Space Complexity: In addition to time, it’s important to consider the memory footprint. DFS, for instance, can be memory-intensive in deep recursion.

4. Applications of Search Algorithms

Search algorithms are not just theoretical concepts but have a wide array of real-world applications:

  • Databases: Search algorithms power the indexing and querying systems in databases, making it possible to retrieve records quickly.
  • Web Search Engines: Search engines like Google use complex search algorithms and optimization techniques to rank pages and provide relevant results.
  • Networking: Algorithms like BFS and DFS are used in networking to explore the connectivity of nodes and find shortest paths.
  • AI and Robotics: Search algorithms are used in pathfinding, such as guiding autonomous robots or AI agents in video games.

5. Conclusion

Search algorithms are indispensable in computer science. They come in various forms, from simple linear and binary searches to more complex graph traversal techniques like DFS and BFS. Understanding the characteristics of each algorithm is critical to selecting the best one for a given problem. Whether you’re searching through a small array or traversing a massive graph, there’s a search algorithm optimized for the task.

By knowing when and how to use these algorithms, developers can build efficient systems that handle even the largest data sets with ease.

 

DALL·E 2024-09-30 14.21.06 - An abstract, clean, and modern representation of stacks and queues in Python. The image should feature a split design_ one side illustrating a stack w

stack, queue in python

Stacks and queues are two of the most fundamental data structures in computer science. Understanding how they work and knowing how to implement them efficiently is essential for solving a wide variety of problems, especially when working with algorithms and data processing. In this article, we will explore the theoretical concepts behind stacks and queues, dive into their operations, and implement both structures using Python with practical examples.

1. What Are Stacks and Queues?

Both stacks and queues are linear data structures that store elements in a sequence. However, they differ in the way elements are inserted and removed:

  • Stack: Follows a Last In, First Out (LIFO) principle. This means the last element added to the stack is the first one to be removed.
  • Queue: Follows a First In, First Out (FIFO) principle. This means the first element added to the queue is the first one to be removed.

Both structures are used extensively in programming and algorithms due to their simple yet powerful operations.


2. Understanding Stacks

A stack can be visualized as a pile of plates. You can only add a new plate on top of the stack, and when you need to remove one, you take the plate from the top. This is precisely how the LIFO principle works in a stack.

2.1 Operations in a Stack

There are four key operations associated with stacks:

  1. Push: Adds an element to the top of the stack.
  2. Pop: Removes the top element from the stack.
  3. Peek/Top: Views the top element without removing it.
  4. isEmpty: Checks whether the stack is empty.

2.2 Implementation of a Stack in Python

Python’s built-in list can be used to implement a stack, as the append() method mimics the push operation, and pop() mimics the pop operation.

class Stack:
    def __init__(self):
        self.stack = []

    def push(self, item):
        self.stack.append(item)

    def pop(self):
        if not self.is_empty():
            return self.stack.pop()
        else:
            return "Stack is empty"

    def peek(self):
        if not self.is_empty():
            return self.stack[-1]
        else:
            return "Stack is empty"

    def is_empty(self):
        return len(self.stack) == 0

    def size(self):
        return len(self.stack)
 

 

Let’s test the implementation with some examples:

 my_stack = Stack()
my_stack.push(10)
my_stack.push(20)
my_stack.push(30)
print(my_stack.peek())  # Output: 30
print(my_stack.pop())   # Output: 30
print(my_stack.pop())   # Output: 20
print(my_stack.is_empty())  # Output: False

The stack starts empty, and after pushing three values (10, 20, 30), the peek function shows the top value, 30. After popping twice, the stack now contains only one element.

2.3 Practical Applications of Stacks

Stacks are useful in various programming situations:

  • Function Call Management: Programming languages often use stacks to manage function calls. When a function is called, its details are pushed onto the stack, and once it completes, it is popped from the stack.
  • Undo Functionality: Applications like text editors use stacks to implement the undo feature. Each operation is pushed onto the stack, and when the user wants to undo, the last operation is popped off.
  • Balancing Parentheses: A stack can be used to check if the parentheses in an expression are balanced.

2.4 Example: Checking Balanced Parentheses

Here’s a simple program that uses a stack to check if a string has balanced parentheses:

def is_balanced(expression):
stack = Stack()
for char in expression:
if char in “({[“:
stack.push(char)
elif char in “)}]”:
if stack.is_empty():
return False
top = stack.pop()
if not matches(top, char):
return False
return stack.is_empty()

def matches(opening, closing):
return (opening == ‘(‘ and closing == ‘)’) or \
(opening == ‘{‘ and closing == ‘}’) or \
(opening == ‘[‘ and closing == ‘]’)

print(is_balanced(“{[()]}”)) # Output: True
print(is_balanced(“{[(])}”)) # Output: False


3. Understanding Queues

A queue can be visualized as a line of people waiting to be served. The person who joins the queue first is the first to be served, which follows the FIFO principle.

3.1 Operations in a Queue

A queue has the following operations:

  1. Enqueue: Adds an element to the end of the queue.
  2. Dequeue: Removes the element from the front of the queue.
  3. Peek/Front: Views the front element without removing it.
  4. isEmpty: Checks if the queue is empty.

3.2 Implementation of a Queue in Python

Using Python’s collections.deque, which provides efficient operations for both ends of a queue, we can implement a queue efficiently.

 from collections import deque

class Queue:
    def __init__(self):
        self.queue = deque()

    def enqueue(self, item):
        self.queue.append(item)

    def dequeue(self):
        if not self.is_empty():
            return self.queue.popleft()
        else:
            return "Queue is empty"

    def peek(self):
        if not self.is_empty():
            return self.queue[0]
        else:
            return "Queue is empty"

    def is_empty(self):
        return len(self.queue) == 0

    def size(self):
        return len(self.queue)

3.3 Example Usage

 my_queue = Queue()
my_queue.enqueue(10)
my_queue.enqueue(20)
my_queue.enqueue(30)
print(my_queue.peek())  # Output: 10
print(my_queue.dequeue())  # Output: 10
print(my_queue.dequeue())  # Output: 20
print(my_queue.is_empty())  # Output: False

This simple queue implementation allows us to add elements to the back and remove elements from the front, following the FIFO principle.

3.4 Practical Applications of Queues

Queues are extremely useful in many real-world applications:

  • Task Scheduling: Queues are used to manage tasks in operating systems, especially in CPU scheduling and disk scheduling.
  • Breadth-First Search (BFS): In graph traversal algorithms like BFS, a queue is used to explore nodes level by level.
  • Print Spooler: In a printer queue, documents are printed in the order they were sent.

4. Comparison Between Stacks and Queues

Both stacks and queues have their uses, but they are suited to different types of problems.

  • Stack is useful when you need to manage data in a LIFO fashion. This is helpful in scenarios like backtracking (e.g., navigating backward in a web browser) and parsing expressions.
  • Queue is helpful when you need to process data in a FIFO manner. This is used in scheduling problems, like managing customer service requests or simulating real-world queues.
FeatureStack (LIFO)Queue (FIFO)
InsertionElements are added to the top.Elements are added to the back.
RemovalElements are removed from the top.Elements are removed from the front.
UsageFunction calls, undo/redo operations.Scheduling tasks, BFS, managing queues.

5. Stacks and Queues in Python Libraries

Although we demonstrated implementing stacks and queues manually, Python provides built-in data structures that make the process more efficient:

  • Stack: Can be implemented using list or collections.deque for fast access.
  • Queue: The queue module provides implementations for different types of queues, such as:
    • Queue: FIFO queue.
    • LifoQueue: LIFO queue (similar to a stack).
    • PriorityQueue: A queue where each element is associated with a priority.
 from queue import Queue

q = Queue()
q.put(10)
q.put(20)
print(q.get())  # Output: 10

Conclusion

Understanding and implementing stacks and queues is an essential skill for any programmer. These simple yet powerful data structures are used in various applications, from operating systems to algorithms. Whether you use Python’s built-in tools or implement them from scratch, mastering these structures will enhance your problem-solving capabilities and deepen your understanding of data management.

By practicing with real-world examples and applying stacks and queues in different scenarios, you’ll gain confidence in knowing when and how to use these versatile data structures.

Screenshot 2024-09-29 104919

multiple online x-o part-2

we talked in the last article about initializing the x-o game in this article we will create the server. we will use express.js and socket.io.

initializing server

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

var waitingplayer=[];
var roomcodearray=[];
var roomplayers={}
//var number=0;

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/x-o1.html');
}); 
server.listen(3000, () => {
  console.log('listening on *:3000');
});

we write in frontend io() to open connection and in back end

 io.on('connection', (socket) => {
//write the the functions here
/*socket.on("event_name",function(){
io.emit("event_name",data_to_send);
})*/
})


// at client
 socket.on("getplayerdetail",(msg)=>{
    roomcode=msg.room;
    playernum=msg.num;
    waiting=false;
    loadbox.style.display="none"
    cont.style.display="block"
    if(playerrole%2==playernum){
        turn.innerHTML="your turn"
    }else{
        turn.innerHTML="wait your turn"
    }
        console.log(msg);
})

continer.onclick=(e)=>{
    console.log(playerrole)
    console.log(waiting)
    if(playerrole%2==playernum && !waiting){
    socket.emit("newgame",{
        pos:e.target.id
    })
    }
}
//at server
//we use io.to to send this data only to this user
io.to(usercode.id).emit("getplayerdetail",   
 {num:0,      room:roomcode});   
 io.to(usercode2.id).emit("getplayerdetail",  
  {num:1,      room:roomcode}      );

here is the full code

 //x-o.html
<!DOCTYPE html>
<html>
    <head>
        <title>x-o</title>
        <style>
          .box{
              height: 150px;
              width:150px;
              background-color: aquamarine;
              text-align: center;
              font-size: 75px;
          }
          #cont{
              width:500px;
             margin:0 auto;
          }
          .continer{
              display:grid;
              grid-gap: 15px;
              grid-template-columns: auto auto auto;
          }
          .score{
              display:inline-block;
              
          }
          .player{
            display:inline-block;
            margin-left: 50px;
            margin-top: 0;
          }
          #getname{
              text-align:center;
              padding-top: 250px;
              padding-bottom: 250px
              
          }
          .boxc{
            box-shadow: 0 8px 16px 0   #A6EEF7  ;
            background-color: cornsilk;
            font-size:20px;
           
           border: solid black;
           width:50%;
           margin-left: 25%;
           margin-right: 25%;
           position: absolute;
           top:40%;
           right:0%;
          }
          #cont,#endbox,#wretry,#disconnect{
            display: none; 
          }
          #btn{
              margin-bottom: 5px;
          }
          #load{
            border: 16px solid #f3f3f3;
  border-radius: 50%;
  border-top: 16px solid #4af307;
  width: 120px;
  height: 120px;
  margin:0 auto;
  animation: load 2s linear infinite;
          }
          @keyframes load {
              0%{ transform: rotate(0deg); }
              100%{ transform: rotate(360deg); }
          }
        </style>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

    </head>
    <body>
       
        
        <div id="cont" >
            <h1 id="turn"></h1>
            <h1 class="player" id="player1">player1(x): <p class="score" id="score1"></p></h1>
        <h1 class="player" id="player2">player2(o): <p class="score" id="score2"></p></h1>
            <div class="continer ">
                
                <div class="box" id="0-0" ></div>
                <div class="box" id="0-1" ></div>
                <div class="box" id="0-2" ></div>
            
        
                <div class="box" id="1-0" ></div>
                <div class="box" id="1-1" ></div>
                <div class="box" id="1-2" ></div>
            
        
                <div class="box" id="2-0" ></div>
                <div class="box" id="2-1" ></div>
                <div class="box" id="2-2" ></div>
            
            </div>
            </div>

           
             
              <div id="loadbox" class="boxc" >
                  <div id="load" >

                  </div>
                <p>wait player to play with him </p>
            </div>
            
            <div id="disconnect" class="boxc" >
            
              <p>your friend disconnect</p>
              <input id="newg1" type="button" value="new game" class=" btn-success" />
          </div>
                <div id="endbox" class="boxc">
                    
                    
                    <p id="plname" style="text-align: center;">wait player to play with him </p>
                    <br/>
                    <div style="text-align:center">
                    <input id="newg" type="button" value="new game" class=" btn-success" />
                    <input id="reg"  type="button" value="retrygame" class=" btn-success" />
                </div>
                </div>
                <div id="wretry" class="boxc" >
                    
                    
                    <p id="want" style="text-align: center;">the another player want to play again with you </p>
                    <br/>
                    <div style="text-align:center">
                    <input id="yes" type="button" value="yes" class=" btn-success" />
                    <input id="no"  type="button" value="no" class=" btn-danger" />
                </div>
<br/>
                </div>
<script src="/socket.io/socket.io.js"></script>

<script>
let reg=document.getElementById("reg");
let newg=document.getElementById("newg");
let newg1=document.getElementById("newg1");
let no=document.getElementById("no");
let yes=document.getElementById("yes");
let wretry=document.getElementById("wretry");
let endbox=document.getElementById("endbox");
let loadbox=document.getElementById("loadbox");
let cont=document.getElementById("cont");
let disconnect=document.getElementById("disconnect");
let winnerp=document.getElementById("plname");
let score1=document.getElementById("score1");
let score2=document.getElementById("score2")
let turn=document.getElementById("turn");
let playernum=0;
let playerrole=0;

let rowofbox={
    "0":0,
    "1":0,
    "2":0
}

let columnofbox={
    "0":0,
    "1":0,
    "2":0
}

let crossofbox={
    "0":0,
    "1":0
}

let gameover=false;

let winner="";
checkhappen=false

function alertwinner(text){
        console.log("here")
        console.log(text)
        endbox.style.display='block';
        winnerp.innerHTML=text;
    }

function retrygame(){
    playerrole=0
    wretry.style.display="none";
    endbox.style.display='none';
    loadbox.style.display='none';
    disconnect.style.display='none';
    rowofbox={
    "0":0,
    "1":0,
    "2":0
    }
    columnofbox={
    "0":0,
    "1":0,
    "2":0
    }
    crossofbox={
    "0":0,
    "1":0
    }
    gameover=false;
    winner=""
    boxes=document.getElementsByClassName("box")
    for(box of boxes){
        box.innerHTML=""
        box.data=false
    }
}

function check(number,ch,player,score){
    if( number == ch){
            gameover=true
            checkhappen=true
            winner=player
            score.innerHTML=Number(score.innerHTML)+1
            //alert(`${winner} is win`)
            
            alertwinner(`${winner} is win`)
            //retrygame()
            return true
        }
    return false
}


function endgame(box,m){
    
    if(m==0){
        var finish=3
        var add=1
        var player="player1"
        var score=score1
    }else{
        var finish=-3
        var add=-1
        var player="player2"
        var score=score2
    }
    row=box.id[0]
    column=box.id[2]


    if(row == column){
        crossofbox["0"]+=add
       
        
        if(row=="1"){
            crossofbox["1"]+=add
        }

    }

    else if(box.id=="2-0" || box.id=="0-2"){
        crossofbox["1"]+=add
    }
    rowofbox[row]+=add
    columnofbox[column]+=add


    let arr=[crossofbox["0"],
    crossofbox["1"],
    rowofbox[row],
    columnofbox[column]]
    for (const ch of arr){
        c=check(ch,finish,player,score);
        if(c){
            break;
        }
    }
    
}

function game(box){
    
    if(!box.data && !gameover && box.className=="box"){
        if(playerrole%2==0){
            box.innerHTML="x"
            endgame(box,0)
    }
        else{
            box.innerHTML="o"
            endgame(box,1)
    }
    if(checkhappen){
        checkhappen=false
    }else{
    playerrole++
    if(playerrole%2==playernum){
        turn.innerHTML="your turn"
    }else{
        turn.innerHTML="wait your turn"
    }
    box.data=true
    }
    if(playerrole>=9){
        alert(`draw`)
        gameover=true
        //retrygame()
    }
}
}

newg.onclick=()=>{
    location.reload();

}
newg1.onclick=()=>{
    location.reload();

}


let continer=document.getElementsByClassName("continer")[0];



let socket = io();
let roomcode="";

var waiting=true;
socket.on("getplayerdetail",(msg)=>{
    roomcode=msg.room;
    playernum=msg.num;
    waiting=false;
    loadbox.style.display="none"
    cont.style.display="block"
    if(playerrole%2==playernum){
        turn.innerHTML="your turn"
    }else{
        turn.innerHTML="wait your turn"
    }
        console.log(msg);
})

continer.onclick=(e)=>{
    console.log(playerrole)
    console.log(waiting)
    if(playerrole%2==playernum && !waiting){
    socket.emit("newgame",{
        pos:e.target.id
    })
    }
}
socket.on("recivenewgame",(data)=>{
  var box=document.getElementById(data);
  console.log(data)
  game(box)
})
socket.on('wantretry',()=>{
    wretry.style.display="block";
    no.onclick=()=>{
        socket.emit("retry","no")
    }
    yes.onclick=()=>{
        socket.emit("retry","yes")
    }
    
})

socket.on("retry",(data)=>{
        if(data==="yes"){
            console.log(data)
            retrygame();
        }else{
            location.reload();
        }
    })
    socket.on("disconnection",()=>{
        /*window.open("http://localhost:3000/start")*/
        wretry.style.display="none";
    endbox.style.display='none';
        disconnect.style.display='block';
        
    })
    
reg.onclick=()=>{
    socket.emit("wantretry")
}
            </script>
    </body>
</html>
//server.js
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

var waitingplayer=[];
var roomcodearray=[];
var roomplayers={}
//var number=0;

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/x-o.html');
});


io.on('connection', (socket) => {
  var usercode=socket;

  
  if (waitingplayer.length===0){
    waitingplayer.push(usercode)
  
  }else{
    var usercode2=waitingplayer.pop();
    var roomcode=`${usercode.id}`+`${usercode2.id}`;
    roomcodearray.push(roomcode);
    usercode.join( roomcode);
    usercode2.join( roomcode);
    roomplayers[usercode.id]={playernum:0,
      roomcode:roomcode,
      socket:usercode
}
roomplayers[usercode2.id]={playernum:1,
  roomcode:roomcode,
  socket:usercode2
}
    io.to(usercode.id).emit("getplayerdetail",
    {num:0,
      room:roomcode});
    io.to(usercode2.id).emit("getplayerdetail",
    {num:1,
      room:roomcode}
      );
  }

  socket.on("newgame",(data)=>{
    io.in(roomplayers[socket.id].roomcode).emit("recivenewgame",data.pos);
  })
  socket.on("wantretry",(data)=>{
    socket.broadcast.to(roomplayers[socket.id].roomcode).emit("wantretry");
  })
  socket.on("retry",(data)=>{
    console.log(data)
    io.in(roomplayers[socket.id].roomcode).emit("retry",data);
  })

  socket.on('disconnect', function () {
    console.log("disconnect")
    if(roomplayers[socket.id] != undefined){
    io.in(roomplayers[socket.id].roomcode).emit("disconnection");
    }
    });
});

server.listen(3000, () => {
  console.log('listening on *:3000');
});
Screenshot 2024-09-29 104919

multiple online x-o game part-1

You cannot use HTTP protocol If you try to create chat applications or multiple games. Because you will need real-time changes and HTTP protocol close the connection after the request and create a new connection every request and respond.
In this case, you will use WebSocket. WebSocket is bidirectional when the connection between client and server the connection will be alive until the client or server terminates it.

There are many libraries to work with this protocol like socket.io. at this article, we will make an online multiple x-o using socket.io and node.js.

the ground

x-o is made of 9 blocks we will use a grid and every block will be div id for every block will represent the position. Assume the 9 blocks are a 3×3 matrix.

 <div id="cont" >
            <h1 id="turn"></h1>
            <h1 class="player" id="player1">player1(x): <p class="score" id="score1"></p></h1>
        <h1 class="player" id="player2">player2(o): <p class="score" id="score2"></p></h1>
            <div class="continer ">
                
                <div class="box" id="0-0" ></div>
                <div class="box" id="0-1" ></div>
                <div class="box" id="0-2" ></div>
            
        
                <div class="box" id="1-0" ></div>
                <div class="box" id="1-1" ></div>
                <div class="box" id="1-2" ></div>
            
        
                <div class="box" id="2-0" ></div>
                <div class="box" id="2-1" ></div>
                <div class="box" id="2-2" ></div>
            
            </div>
            </div>

           
             
              <div id="loadbox" class="boxc" >
                  <div id="load" >

                  </div>
                <p>wait player to play with him </p>
            </div>
            
            <div id="disconnect" class="boxc" >
            
              <p>your friend disconnect</p>
              <input id="newg1" type="button" value="new game" class=" btn-success" />
          </div>
                <div id="endbox" class="boxc">
                    
                    
                    <p id="plname" style="text-align: center;">wait player to play with him </p>
                    <br/>
                    <div style="text-align:center">
                    <input id="newg" type="button" value="new game" class=" btn-success" />
                    <input id="reg"  type="button" value="retrygame" class=" btn-success" />
                </div>
                </div>
                <div id="wretry" class="boxc" >
                    
                    
                    <p id="want" style="text-align: center;">the another player want to play again with you </p>
                    <br/>
                    <div style="text-align:center">
                    <input id="yes" type="button" value="yes" class=" btn-success" />
                    <input id="no"  type="button" value="no" class=" btn-danger" />
                </div>
<br/>
                </div>
  <style>
          .box{
              height: 150px;
              width:150px;
              background-color: aquamarine;
              text-align: center;
              font-size: 75px;
          }
          #cont{
              width:500px;
             margin:0 auto;
          }
          .continer{
              display:grid;
              grid-gap: 15px;
              grid-template-columns: auto auto auto;
          }
          .score{
              display:inline-block;
              
          }
          .player{
            display:inline-block;
            margin-left: 50px;
            margin-top: 0;
          }
          #getname{
              text-align:center;
              padding-top: 250px;
              padding-bottom: 250px
              
          }
          .boxc{
            box-shadow: 0 8px 16px 0   #A6EEF7  ;
            background-color: cornsilk;
            font-size:20px;
           
           border: solid black;
           width:50%;
           margin-left: 25%;
           margin-right: 25%;
           position: absolute;
           top:40%;
           right:0%;
          }
          #cont,#endbox,#wretry,#disconnect{
            display: none; 
          }
          #btn{
              margin-bottom: 5px;
          }
          #load{
            border: 16px solid #f3f3f3;
  border-radius: 50%;
  border-top: 16px solid #4af307;
  width: 120px;
  height: 120px;
  margin:0 auto;
  animation: load 2s linear infinite;
          }
          @keyframes load {
              0%{ transform: rotate(0deg); }
              100%{ transform: rotate(360deg); }
          }
        </style>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

Initialize

we will make three arrays. array for a row, array for a column, and array for a cross. we have three rows of the array for each row so the row array’s length is three and also for each column. for cross, there are only 2.

let reg=document.getElementById("reg");
let newg=document.getElementById("newg");
let newg1=document.getElementById("newg1");
let no=document.getElementById("no");
let yes=document.getElementById("yes");
let wretry=document.getElementById("wretry");
let endbox=document.getElementById("endbox");
let loadbox=document.getElementById("loadbox");
let cont=document.getElementById("cont");
let disconnect=document.getElementById("disconnect");
let winnerp=document.getElementById("plname");
let score1=document.getElementById("score1");
let score2=document.getElementById("score2");
let turn=document.getElementById("turn");
let playernum=0;
let playerrole=0;
let rowofbox={
    "0":0,
    "1":0,
    "2":0
}

let columnofbox={
    "0":0,
    "1":0,
    "2":0
}

let crossofbox={
    "0":0,
    "1":0
}

let gameover=false;

let winner="";
checkhappen=false 
function alertwinner(text){
        console.log("here")
        console.log(text)
        endbox.style.display='block';
        winnerp.innerHTML=text;
    }

function retrygame(){
    playerrole=0
    wretry.style.display="none";
    endbox.style.display='none';
    loadbox.style.display='none';
    disconnect.style.display='none';
    rowofbox={
    "0":0,
    "1":0,
    "2":0
    }
    columnofbox={
    "0":0,
    "1":0,
    "2":0
    }
    crossofbox={
    "0":0,
    "1":0
    }
    gameover=false;
    winner=""
    boxes=document.getElementsByClassName("box")
    for(box of boxes){
        box.innerHTML=""
        box.data=false
    }
}

function check(number,ch,player,score){
    if( number == ch){
            gameover=true
            checkhappen=true
            winner=player
            score.innerHTML=Number(score.innerHTML)+1
            //alert(`${winner} is win`)
            
            alertwinner(`${winner} is win`)
            //retrygame()
            return true
        }
    return false
}


function endgame(box,m){
    
    if(m==0){
        var finish=3
        var add=1
        var player="player1"
        var score=score1
    }else{
        var finish=-3
        var add=-1
        var player="player2"
        var score=score2
    }
    row=box.id[0]
    column=box.id[2]


    if(row == column){
        crossofbox["0"]+=add
       
        
        if(row=="1"){
            crossofbox["1"]+=add
        }

    }

    else if(box.id=="2-0" || box.id=="0-2"){
        crossofbox["1"]+=add
    }
    rowofbox[row]+=add
    columnofbox[column]+=add


    let arr=[crossofbox["0"],
    crossofbox["1"],
    rowofbox[row],
    columnofbox[column]]
    for (const ch of arr){
        c=check(ch,finish,player,score);
        if(c){
            break;
        }
    }
    
}

function game(box){
    
    if(!box.data && !gameover && box.className=="box"){
        if(playerrole%2==0){
            box.innerHTML="x"
            endgame(box,0)
    }
        else{
            box.innerHTML="o"
            endgame(box,1)
    }
    if(checkhappen){
        checkhappen=false
    }else{
    playerrole++
    if(playerrole%2==playernum){
        turn.innerHTML="your turn"
    }else{
        turn.innerHTML="wait your turn"
    }
    box.data=true
    }
    if(playerrole>=9){
        alert(`draw`)
        gameover=true
        //retrygame()
    }
}
}

newg.onclick=()=>{
    location.reload();

}
newg1.onclick=()=>{
    location.reload();

}


let continer=document.getElementsByClassName("continer")[0];

in this code, we initialize the game and write the algorithm to determine who is the winner. at the endgame function, there are two parameters (m) parameter is 0 or 1 if 0 that means player 1 played. the parameter box is the block which clicked by the player.

when clicking the block this function will run if the block is in row 1 and column 2 that means column[2[ and row[1] values will change if player 1 then will add 1 if player 2 then will add -1 if the value I and position in column or row or cross array is 3 or -3 that means one of the two players is the winner if 3 that mean player 1 if -3 that mean player 2.

we will talk about the back end in the next article

Untitled design

SQL VS NoSQL

Introduction

If you want to save data to go back to when you need to use it in the future, how will you save it? Maybe you will say I will write it in a text file. But if you have a large data it will be difficult to fetch it. So you need another way to organize data and save it.

There are several ways, you can organize your data as a Hierarchy or you can organize it in tables and make relations between tables like an Excel sheet. We called the second-way relational database.

Relational database

They arose out of Edgar Codd’s 1970 paper “A Relational Model of Data for Large Shared Data Banks”.

As said before we can Think of the relational database as a collection of spreadsheet files that help businesses organize, manage, and relate data. In the relational database model, each “spreadsheet” is a table that stores information, represented as columns (attributes) and rows (records or tuples). There is some important terminology to understand the relation data model:

1- Entity: is an object and has attributes for example you have a database for a company in the company you have employees so you will create an employee entity and give it attributes like ID, age, salary, shift….

Employee
ID
Age
Salary
Shift

2- relation: is a table or file, is a subset of the Cartesian product of a list of domains.

3-domain: A domain is the original set of atomic values used to model data. By atomic value, we mean that each value in the domain is indivisible as far as the relational model is concerned. For example, the domain for shift row is {Monday, Thursday, Wednesday, …, Sunday}.

SQL

“It provides a means of describing data with its natural structure only-that is, without superimposing any additional structure for machine representation purposes. Accordingly, it provides a basis for a high level data language which will yield maximal independence between programs on the one hand and machine representation and organization of data on the other.” Codd[1970].

SQL is a computer language that you use to interact with a rational database. SQL is not  a database management system (we will talk about database management system in another article)  SQL is a language to contact with database management system or database engine.

NoSQL

We meant by NoSQL non-relational database. we have four type

1-Key-value:

like DynamoDB. save the data as items containing key  and value

2- document-oriented:

like Firebase and MongoDB. save the data as JSON

3- wide-column:

like HBase. save data as a table but not like a rational database

4- graph:

like Neo4J. save data as a graph data structure.

NoSQLSQL
Uses many types of data model depends on the type of No SQLuses a tabular data structure
provides a flexible schemafixed schema
There is many language depends on the type of NoSQL MongoDB uses MQLuses structured query language
Relationships can be nested, explicit, or implicit.Relationships are defined through foreign keys and accessed using joins.
suitable for real-time processing, big data analytics, and distributed environments.suitable for read-heavy and transaction workloads

Advantages of NoSQL

1- High Scalability

NoSQL databases can expand out by employing commodity hardware as opposed to scaling up by adding more servers. This can handle more traffic to meet demand without experiencing any downtime. NoSQL databases are now the go-to choice for growing data sets since they can grow bigger and more capable by scaling out.

2- High Availability

NoSQL databases automatically replicate data across multiple servers, data centers, or cloud resources. In turn, this minimizes latency for users, no matter where they’re located. This feature also works to reduce the burden of database management, which frees up time to focus on other priorities.

3- High performance

A NoSQL database’s scale-out architecture might be especially helpful when traffic or data volume rises. This architecture guarantees single-digit millisecond response times that are both predictable and quick, as illustrated in the image below. NoSQL databases are utilized in applications that need to provide a highly interactive user experience but also collect terabytes of data every day since they can consume data and distribute it fast and reliably. The next figure illustrates two different incoming rates: 150 writes per second (green line) and 300 reads per second (blue line) with 95th latency values in the 3–4 ms region.

4- Highly Functional

Distributed data stores with very high data storage requirements are the target market for NoSQL databases. Because of this, NoSQL is the best option for applications with huge data, real-time web apps, customer 360, online gaming, online shopping, the Internet of things, social networks, and online advertising.

5- flexability

Data is stored in a much more strict, specified structure when using SQL databases. However, NoSQL eliminates the need for those strict schemas and allows for more flexible data storage. Innovation and quick application development are made possible by this design. Without having to worry about schemas, developers can concentrate on building solutions that will better serve their clients. Any type of data, including unstructured, semi-structured, and structured data, can be readily handled by NoSQL databases in a single data repository.

Disadvantages of NoSQL

1- Management Challenge:

Big data management in NoSQL is much more complex than a relational database.

2- Backup:

it is a great weak point for some NoSQL

When should use NoSQL

1- The relationship between the data you store is not that important.

2- data changes over time and is not structured

Reference

https://www.mongodb.com/resources/basics/databases/nosql-explained

https://www.coursera.org/articles/nosql-vs-sql

SQL The Complete Reference, Third Edition. Weinberg Paul, Groff James, Oppel Andrew

 A Relational Model of Data for Large Shared Data Banks , E. F. CODD,1970

Database systems, C.foster,Elvis.

M.Sc. (Computer Science)  Sem-I Database Technologies Chapter 1 Introduction to NOSQL.