Bongani  Ngema

Bongani Ngema

1669293240

How to Code A Rock Paper Scissors Game using JavaScript

Coding a rock paper scissors game with JavaScript, HTML, and CSS

This tutorial will help you learn how to code a rock paper scissors game using JavaScript as its main engine.

The end result of this tutorial is as follows:

Screenshot of rock paper scissors game with JavaScriptScreenshot of rock paper scissors game with JavaScript

The complete code is stored in the following GitHub repository. You can play the game on this GitHub Page.

Let’s start with creating the core game mechanism using JavaScript and HTML first.

Rock paper scissors game using JavaScript and HTML

To create the code mechanics of the rock, paper, and scissors game, you need to create three HTML <button> elements with each represents an option for the user.

You can create them as follows:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
</body>

Next, you need to grab the elements using JavaScript and add an event listener to each one of them. The querySelectorAll() method is used because the returned NodeList object implements the forEach() method.

Take a look at the code below:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
  <script>
    const options = document.querySelectorAll(".options");

    options.forEach((option) => {
      option.addEventListener("click", function () {
        // TO DO: process user click
      });
    });
  </script>
</body>

The callback function passed into addEventListener() method will be executed when the user clicks on one of the three buttons.

When a click event is triggered, you need to fetch the user’s selected option by reading the value of the textContent property. Let’s store the value in a variable called pInput which stands for Player Input:

const pInput = this.textContent;

The this keyword in the code above refers to the <button> element that was clicked by the user. If the user clicked on "Rock" button, then pInput variable value will be of "Rock" string.

Once you have the pInput, it’s time to get the computer selection. The computer needs to select one of the three available choices.

You can use the Math.random() method to generate a random number between 0-2 and use it to randomly select between rock, paper, or scissors for the computer.

Here’s how you do it:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
  <script>
    const options = document.querySelectorAll(".options");

    options.forEach((option) => {
      option.addEventListener("click", function () {
        const pInput = this.textContent;

        const cOptions = ["Rock", "Paper", "Scissors"];
        const cInput = cOptions[Math.floor(Math.random() * 3)];
      });
    });
  </script>
</body>

The cInput variable stands for Computer Input.

Now that you have both pInput and cInput ready, it’s time to create a function to compare them and find out who wins the current round.

Let’s call the function compareInputs() and start by creating an if statement that ends the round with a tie:

function compareInputs(pInput, cInput) {
  const currentMatch = `${pInput} vs ${cInput}`;

  // Tie check
  if (pInput === cInput) {
    alert(`${currentMatch} is a Tie`);
    return;
  }
}

When the inputs are not equal, you can start checking pInput value if it matches one of the three possible options. The code below starts with checking if the user selects "Rock" for the current round:

function compareInputs(pInput, cInput) {
  const currentMatch = `${pInput} vs ${cInput}`;

  // Tie check
  if (pInput === cInput) {
    alert(`${currentMatch} is a Tie`);
    return;
  }

  // Rock
  if (pInput === "Rock") {
    if (cInput === "Scissors") {
      alert(`${currentMatch} = You Win`);
    } else {
      alert(`${currentMatch} = Computer Wins`);
    }
  }
}

Since you’ve checked for the possibility of a tie first, you only need to create one if..else block for the outcome.

After that, you need to check for both "Paper" and "Scissors" as follows:

function compareInputs(pInput, cInput) {
  const currentMatch = `${pInput} vs ${cInput}`;

  // Tie check
  if (pInput === cInput) {
    alert(`${currentMatch} is a Tie`);
    return;
  }

  // Rock
  if (pInput === "Rock") {
    if (cInput === "Scissors") {
      alert(`${currentMatch} = You Win`);
    } else {
      alert(`${currentMatch} = Computer Wins`);
    }
  }
  // Paper
  else if (pInput === "Paper") {
    if (cInput === "Rock") {
      alert(`${currentMatch} = You Win`);
    } else {
      alert(`${currentMatch} = Computer Wins`);
    }
  }
  // Scissors
  else {
    if (cInput === "Paper") {
      alert(`${currentMatch} = You Win`);
    } else {
      alert(`${currentMatch} = Computer Wins`);
    }
  }
}

With the function completed, you just need to call it inside the addEventListener() method:

option.addEventListener("click", function () {
  const pInput = this.textContent;

  const cOptions = ["Rock", "Paper", "Scissors"];
  const cInput = cOptions[Math.floor(Math.random() * 3)];

  compareInputs(pInput, cInput);
});

And with that, you have completed the core mechanics of the rock paper scissors game.

The full code is as shown below:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
  <script>
    const options = document.querySelectorAll(".options");

    options.forEach((option) => {
      option.addEventListener("click", function () {
        const pInput = this.textContent;

        const cOptions = ["Rock", "Paper", "Scissors"];
        const cInput = cOptions[Math.floor(Math.random() * 3)];

        compareInputs(pInput, cInput);
      });
    });

    function compareInputs(pInput, cInput) {
      const currentMatch = `${pInput} vs ${cInput}`;

      // Tie check
      if (pInput === cInput) {
        alert(`${currentMatch} is a Tie`);
        return;
      }

      // Rock
      if (pInput === "Rock") {
        if (cInput === "Scissors") {
          alert(`${currentMatch} = You Win`);
        } else {
          alert(`${currentMatch} = Computer Wins`);
        }
      }
      // Paper
      else if (pInput === "Paper") {
        if (cInput === "Rock") {
          alert(`${currentMatch} = You Win`);
        } else {
          alert(`${currentMatch} = Computer Wins`);
        }
      }
      // Scissors
      else if (pInput === "Scissors") {
        if (cInput === "Paper") {
          alert(`${currentMatch} = You Win`);
        } else {
          alert(`${currentMatch} = Computer Wins`);
        }
      }
    }
  </script>
</body>

Now that you’ve learned the basic mechanics of the rock paper scissors game, it’s time to add more features to improve the gaming experience.

Improving the gaming experience for rock paper scissors game

With the core game mechanism set, you can start improving the game experience by keeping scores. For example, once the user or the computer gains five wins, the winner is called out and the score resets back to zero.

This can be done by creating two variables that keep the scores for both the user and the computer. The variable values will be updated when the user or the computer wins, and another function will check if either variable is equal to five.

Let’s start by updating the HTML with new elements to display the game objective and the scores:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <h2>Win five rounds to be the victor</h2>
  <h3>
    Player <span id="p-score">0</span> - <span id="c-score">0</span> Computer
  </h3>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
</body>

The <span> elements above will be updated with the new variables named pScore and cScore. Let’s write the new variables inside the <script> tag:

const options = document.querySelectorAll(".options");
let pScore = 0;
let cScore = 0;

options.forEach((option) => {
  // ...
});

Next, increment the pScore value by one when the user wins, or the cScore value when the computer wins:

if (pInput === "Rock") {
  if (cInput === "Scissors") {
    alert(`${currentMatch} = You Win`);
    pScore++;
  } else {
    alert(`${currentMatch} = Computer Wins`);
    cScore++;
  }
}
//Check for Paper
else if (pInput === "Paper") {
  if (cInput === "Rock") {
    alert(`${currentMatch} = You Win`);
    pScore++;
  } else {
    alert(`${currentMatch} = Computer Wins`);
    cScore++;
  }
}
//Check for Scissors
else {
  if (cInput === "Paper") {
    alert(`${currentMatch} = You Win`);
    pScore++;
  } else {
    alert(`${currentMatch} = Computer Wins`);
    cScore++;
  }
}

Once the scores are updated, call a new function to update the scores displayed in the browser. Let’s call the function updateScore() method:

options.forEach((option) => {
  option.addEventListener("click", function () {
    const pInput = this.textContent;

    const computerOptions = ["Rock", "Paper", "Scissors"];
    const cInput = computerOptions[Math.floor(Math.random() * 3)];

    compareInputs(pInput, cInput);
    updateScore();
  });
});

The code inside the updateScore() method will update the <span> elements you’ve added before:

function updateScore() {
  document.getElementById("pScore").textContent = pScore;
  document.getElementById("cScore").textContent = cScore;
}

Finally, you need another function to check if one of the players already gained five wins. Let’s create another function named checkWinner() to do just that:

function checkWinner() {
  if (pScore === 5 || cScore === 5) {
    const winner =
      pScore === 5
        ? "You win the game! Congratulations!"
        : "Computer wins the game! Try again next time!";
    alert(winner);
    return true;
  }
  return false;
}

Call the checkWinner() function right above the call to updateScore() function. When it returns true, set the scores back to zero and call updateScore() again to refresh the rendered scores:

compareInputs(pInput, cInput);
updateScore();
if (checkWinner()) {
  pScore = cScore = 0;
  updateScore();
}

Now your rock paper scissors game will keep the scores and declare the winner when 5 wins have been gained.

Here’s the complete code again:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <h2>Win five rounds to be the victor</h2>
  <h3>
    Player <span id="p-score">0</span> - <span id="c-score">0</span> Computer
  </h3>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
  <script>
    const options = document.querySelectorAll(".options");
    let pScore = 0;
    let cScore = 0;

    options.forEach((option) => {
      option.addEventListener("click", function () {
        const pInput = this.textContent;

        const cOptions = ["Rock", "Paper", "Scissors"];
        const cInput = cOptions[Math.floor(Math.random() * 3)];

        compareInputs(pInput, cInput);
        updateScore();
        if (checkWinner()) {
          pScore = cScore = 0;
          updateScore();
        }
      });
    });

    function compareInputs(pInput, cInput) {
      const currentMatch = `${pInput} vs ${cInput}`;
      if (pInput === cInput) {
        alert(`${currentMatch} is a Tie`);
        return;
      }

      if (pInput === "Rock") {
        if (cInput === "Scissors") {
          alert(`${currentMatch} = You Win`);
          pScore++;
        } else {
          alert(`${currentMatch} = Computer Wins`);
          cScore++;
        }
      }
      //Check for Paper
      else if (pInput === "Paper") {
        if (cInput === "Rock") {
          alert(`${currentMatch} = You Win`);
          pScore++;
        } else {
          alert(`${currentMatch} = Computer Wins`);
          cScore++;
        }
      }
      //Check for Scissors
      else {
        if (cInput === "Paper") {
          alert(`${currentMatch} = You Win`);
          pScore++;
        } else {
          alert(`${currentMatch} = Computer Wins`);
          cScore++;
        }
      }
    }

    function updateScore() {
      document.getElementById("p-score").textContent = pScore;
      document.getElementById("c-score").textContent = cScore;
    }

    function checkWinner() {
      if (pScore === 5 || cScore === 5) {
        const winner =
          pScore === 5
            ? "You win the game! Congratulations!"
            : "Computer wins the game! Try again next time!";
        alert(winner);
        return true;
      }
      return false;
    }
  </script>
</body>

With that, your rock paper scissors game mechanic is completed.

Up until now, you haven’t touched the user interface of your game, so let’s add some style and make the interface prettier in the next section.

Styling the rock paper scissors game

You can make the game interface better by using CSS to add colors and borders. But the most important improvement would be to replace the <button> elements with images of rock, paper, and scissors.

First, you need to download the images for the actions here.

Note: the images are created by Cristiano Zoucas from Noun Project free of charge

Once you have the buttons, update your HTML <body> tag as shown below:

<body>
  <div class="container">
    <h1>The Rock Paper Scissors Game</h1>
    <h2>Win five rounds to be the victor</h2>
    <h2>
      Player <span id="p-score">0</span> - <span id="c-score">0</span> Computer
    </h2>
    <div class="display-moves">
      <div class="move">
        <img id="p-move" src="./assets/Rock.svg">
      </div>
      <div class="separator">VS</div>
      <div class="move">
        <img id="c-move" src="./assets/Rock.svg">
      </div>
    </div>
    <h2>Click on the image below to select your move:<h2>
    <div class="action">
      <input class="options" type="image" src="./assets/Rock.svg" value="Rock" />
      <input class="options" type="image" src="./assets/Paper.svg" value="Paper" />
      <input class="options" type="image" src="./assets/Scissors.svg" value="Scissors" />
    </div>
  </div>
</body>

The <button> elements are replaced with the <input> elements using the type image. Additional <div> elements are added to the HTML page to help with CSS styling.

The CSS code for the page will be as follows:

body {
  text-align: center;
}
.container {
  margin: 0 auto;
  background-color: aquamarine;
  border-radius: 20px;
  padding: 25px 50px;
  width: 80vw;
}
.action {
  max-width: 800px;
  padding: 40px 0;
  margin: 0 auto;
  background-color: cadetblue;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-evenly;
  border-radius: 20px;
}
.options {
  flex: 1 1 30%;
  border-radius: 50%;
  border-width: 4px;
  border-style: solid;
  border-color: rgba(0, 0, 0, 0.7);
  padding: 10px;
  animation-name: anim-fade;
  animation-duration: 1s;
  animation-fill-mode: forwards;
  width: 100%;
  max-width: 120px;
}
.display-moves {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-evenly;
}
.move {
  border-radius: 50%;
  border-width: 4px;
  border-style: solid;
  border-color: rgba(0, 0, 0, 0.7);
}
.separator {
  font-size: 5em;
}

The CSS above will add background color to the HTML page and create a circle border around the images. The last improvement you need to do is to update the JavaScript code.

First, revise the pInput variable value to fetch from value property instead of textContent:

const pInput = this.value;

After that, create a function to update the player and the computer actions (the smaller images)

Let’s name it the updateMoves() function:

function updateMoves(pInput, cInput) {
  document.getElementById("p-move").src = `./assets/${pInput}.svg`;
  document.getElementById("c-move").src = `./assets/${cInput}.svg`;
}

Finally, call the updateMoves() right before calling the compareInputs() function:

option.addEventListener("click", function () {
  const pInput = this.value;

  const cOptions = ["Rock", "Paper", "Scissors"];
  const cInput = cOptions[Math.floor(Math.random() * 3)];

  updateMoves(pInput, cInput);
  compareInputs(pInput, cInput);
  updateScore();
  if (checkWinner()) {
    pScore = cScore = 0;
    updateScore();
  }
});

Now your rock paper scissors game looks a lot better than before. Here’s a GitHub repository storing the finished game.

Conclusion

You’ve learned how to code a rock paper scissors game using JavaScript, HTML, and CSS.

Optionally, you can still improve the application further by adding an option for users to select how many rounds they want to play the game.

But don’t worry about it and congratulations on making it this far! 😉

Original article source at: https://sebhastian.com/

#javascript #game 

What is GEEK

Buddha Community

How to Code A Rock Paper Scissors Game using JavaScript
Monty  Boehm

Monty Boehm

1675304280

How to Use Hotwire Rails

Introduction

We are back with another exciting and much-talked-about Rails tutorial on how to use Hotwire with the Rails application. This Hotwire Rails tutorial is an alternate method for building modern web applications that consume a pinch of JavaScript.

Rails 7 Hotwire is the default front-end framework shipped with Rails 7 after it was launched. It is used to represent HTML over the wire in the Rails application. Previously, we used to add a hotwire-rails gem in our gem file and then run rails hotwire: install. However, with the introduction of Rails 7, the gem got deprecated. Now, we use turbo-rails and stimulus rails directly, which work as Hotwire’s SPA-like page accelerator and Hotwire’s modest JavaScript framework.

What is Hotwire?

Hotwire is a package of different frameworks that help to build applications. It simplifies the developer’s work for writing web pages without the need to write JavaScript, and instead sending HTML code over the wire.

Introduction to The Hotwire Framework:

1. Turbo:

It uses simplified techniques to build web applications while decreasing the usage of JavaScript in the application. Turbo offers numerous handling methods for the HTML data sent over the wire and displaying the application’s data without actually loading the entire page. It helps to maintain the simplicity of web applications without destroying the single-page application experience by using the below techniques:

Turbo Frames: Turbo Frames help to load the different sections of our markup without any dependency as it divides the page into different contexts separately called frames and updates these frames individually.
Turbo Drive: Every link doesn’t have to make the entire page reload when clicked. Only the HTML contained within the tag will be displayed.
Turbo Streams: To add real-time features to the application, this technique is used. It helps to bring real-time data to the application using CRUD actions.

2. Stimulus

It represents the JavaScript framework, which is required when JS is a requirement in the application. The interaction with the HTML is possible with the help of a stimulus, as the controllers that help those interactions are written by a stimulus.

3. Strada

Not much information is available about Strada as it has not been officially released yet. However, it works with native applications, and by using HTML bridge attributes, interaction is made possible between web applications and native apps.

Simple diagrammatic representation of Hotwire Stack:

Hotwire Stack

Prerequisites For Hotwire Rails Tutorial

As we are implementing the Ruby on Rails Hotwire tutorial, make sure about the following installations before you can get started.

  • Ruby on Rails
  • Hotwire gem
  • PostgreSQL/SQLite (choose any one database)
  • Turbo Rails
  • Stimulus.js

Looking for an enthusiastic team of ROR developers to shape the vision of your web project?
Contact Bacancy today and hire Ruby developers to start building your dream project!

Create a new Rails Project

Find the following commands to create a rails application.

mkdir ~/projects/railshotwire
cd ~/projects/railshotwire
echo "source 'https://rubygems.org'" > Gemfile
echo "gem 'rails', '~> 7.0.0'" >> Gemfile
bundle install  
bundle exec rails new . --force -d=postgresql

Now create some files for the project, up till now no usage of Rails Hotwire can be seen.
Fire the following command in your terminal.

  • For creating a default controller for the application
echo "class HomeController < ApplicationController" > app/controllers/home_controller.rb
echo "end" >> app/controllers/home_controller.rb
  • For creating another controller for the application
echo "class OtherController < ApplicationController" > app/controllers/other_controller.rb
echo "end" >> app/controllers/home_controller.rb
  • For creating routes for the application
echo "Rails.application.routes.draw do" > config/routes.rb
echo '  get "home/index"' >> config/routes.rb
echo '  get "other/index"' >> config/routes.rb
echo '  root to: "home#index"' >> config/routes.rb
echo 'end' >> config/routes.rb
  • For creating a default view for the application
mkdir app/views/home
echo '<h1>This is Rails Hotwire homepage</h1>' > app/views/home/index.html.erb
echo '<div><%= link_to "Enter to other page", other_index_path %></div>' >> app/views/home/index.html.erb
  • For creating another view for the application
mkdir app/views/other
echo '<h1>This is Another page</h1>' > app/views/other/index.html.erb
echo '<div><%= link_to "Enter to home page", root_path %></div>' >> app/views/other/index.html.erb
  • For creating a database and schema.rb file for the application
bin/rails db:create
bin/rails db:migrate
  • For checking the application run bin/rails s and open your browser, your running application will have the below view.

Rails Hotwire Home Page

Additionally, you can clone the code and browse through the project. Here’s the source code of the repository: Rails 7 Hotwire application

Now, let’s see how Hotwire Rails can work its magic with various Turbo techniques.

Hotwire Rails: Turbo Drive

Go to your localhost:3000 on your web browser and right-click on the Inspect and open a Network tab of the DevTools of the browser.

Now click on go to another page link that appears on the home page to redirect from the home page to another page. In our Network tab, we can see that this action of navigation is achieved via XHR. It appears only the part inside HTML is reloaded, here neither the CSS is reloaded nor the JS is reloaded when the navigation action is performed.

Hotwire Rails Turbo Drive

By performing this action we can see that Turbo Drive helps to represent the HTML response without loading the full page and only follows redirect and reindeer HTML responses which helps to make the application faster to access.

Hotwire Rails: Turbo Frame

This technique helps to divide the current page into different sections called frames that can be updated separately independently when new data is added from the server.
Below we discuss the different use cases of Turbo frame like inline edition, sorting, searching, and filtering of data.

Let’s perform some practical actions to see the example of these use cases.

Make changes in the app/controllers/home_controller.rb file

#CODE

class HomeController < ApplicationController
   def turbo_frame_form
   end
   
   def turbo_frame submit
      extracted_anynumber = params[:any][:anynumber]
      render :turbo_frame_form, status: :ok, locals: {anynumber: extracted_anynumber,      comment: 'turbo_frame_submit ok' }
   end
end

Turbo Frame

Add app/views/home/turbo_frame_form.html.erb file to the application and add this content inside the file.

#CODE

<section>

    <%= turbo_frame_tag 'anyframe' do %>
            
      <div>
          <h2>Frame view</h2>
          <%= form_with scope: :any, url: turbo_frame_submit_path, local: true do |form| %>
              <%= form.label :anynumber, 'Type an integer (odd or even)', 'class' => 'my-0  d-inline'  %>
              <%= form.text_field :anynumber, type: 'number', 'required' => 'true', 'value' => "#{local_assigns[:anynumber] || 0}",  'aria-describedby' => 'anynumber' %>
              <%= form.submit 'Submit this number', 'id' => 'submit-number' %>
          <% end %>
      </div>
      <div>
        <h2>Data of the view</h2>
        <pre style="font-size: .7rem;"><%= JSON.pretty_generate(local_assigns) %></pre> 
      </div>
      
    <% end %>

</section>

Add the content inside file

Make some adjustments in routes.rb

#CODE

Rails.application.routes.draw do
  get 'home/index'
  get 'other/index'

  get '/home/turbo_frame_form' => 'home#turbo_frame_form', as: 'turbo_frame_form'
  post '/home/turbo_frame_submit' => 'home#turbo_frame_submit', as: 'turbo_frame_submit'


  root to: "home#index"
end
  • Next step is to change homepage view in app/views/home/index.html.erb

#CODE

<h1>This is Rails Hotwire home page</h1>
<div><%= link_to "Enter to other page", other_index_path %></div>

<%= turbo_frame_tag 'anyframe' do %>        
  <div>
      <h2>Home view</h2>
      <%= form_with scope: :any, url: turbo_frame_submit_path, local: true do |form| %>
          <%= form.label :anynumber, 'Type an integer (odd or even)', 'class' => 'my-0  d-inline'  %>
          <%= form.text_field :anynumber, type: 'number', 'required' => 'true', 'value' => "#{local_assigns[:anynumber] || 0}",  'aria-describedby' => 'anynumber' %>
          <%= form.submit 'Submit this number', 'id' => 'submit-number' %>
      <% end %>
  <div>
<% end %>

Change HomePage

After making all the changes, restart the rails server and refresh the browser, the default view will appear on the browser.

restart the rails serverNow in the field enter any digit, after entering the digit click on submit button, and as the submit button is clicked we can see the Turbo Frame in action in the below screen, we can observe that the frame part changed, the first title and first link didn’t move.

submit button is clicked

Hotwire Rails: Turbo Streams

Turbo Streams deliver page updates over WebSocket, SSE or in response to form submissions by only using HTML and a series of CRUD-like operations, you are free to say that either

  • Update the piece of HTML while responding to all the other actions like the post, put, patch, and delete except the GET action.
  • Transmit a change to all users, without reloading the browser page.

This transmit can be represented by a simple example.

  • Make changes in app/controllers/other_controller.rb file of rails application

#CODE

class OtherController < ApplicationController

  def post_something
    respond_to do |format|
      format.turbo_stream {  }
    end
  end

   end

file of rails application

Add the below line in routes.rb file of the application

#CODE

post '/other/post_something' => 'other#post_something', as: 'post_something'
Add the below line

Superb! Rails will now attempt to locate the app/views/other/post_something.turbo_stream.erb template at any moment the ‘/other/post_something’ endpoint is reached.

For this, we need to add app/views/other/post_something.turbo_stream.erb template in the rails application.

#CODE

<turbo-stream action="append" target="messages">
  <template>
    <div id="message_1">This changes the existing message!</div>
  </template>
</turbo-stream>
Add template in the rails application

This states that the response will try to append the template of the turbo frame with ID “messages”.

Now change the index.html.erb file in app/views/other paths with the below content.

#CODE

<h1>This is Another page</h1>
<div><%= link_to "Enter to home page", root_path %></div>

<div style="margin-top: 3rem;">
  <%= form_with scope: :any, url: post_something_path do |form| %>
      <%= form.submit 'Post any message %>
  <% end %>
  <turbo-frame id="messages">
    <div>An empty message</div>
  </turbo-frame>
</div>
change the index.html.erb file
  • After making all the changes, restart the rails server and refresh the browser, and go to the other page.

go to the other page

  • Once the above screen appears, click on the Post any message button

Post any message button

This action shows that after submitting the response, the Turbo Streams help the developer to append the message, without reloading the page.

Another use case we can test is that rather than appending the message, the developer replaces the message. For that, we need to change the content of app/views/other/post_something.turbo_stream.erb template file and change the value of the action attribute from append to replace and check the changes in the browser.

#CODE

<turbo-stream action="replace" target="messages">
  <template>
    <div id="message_1">This changes the existing message!</div>
  </template>
</turbo-stream>

change the value of the action attributeWhen we click on Post any message button, the message that appear below that button will get replaced with the message that is mentioned in the app/views/other/post_something.turbo_stream.erb template

click on Post any message button

Stimulus

There are some cases in an application where JS is needed, therefore to cover those scenarios we require Hotwire JS tool. Hotwire has a JS tool because in some scenarios Turbo-* tools are not sufficient. But as we know that Hotwire is used to reduce the usage of JS in an application, Stimulus considers HTML as the single source of truth. Consider the case where we have to give elements on a page some JavaScript attributes, such as data controller, data-action, and data target. For that, a stimulus controller that can access elements and receive events based on those characteristics will be created.

Make a change in app/views/other/index.html.erb template file in rails application

#CODE

<h1>This is Another page</h1>
<div><%= link_to "Enter to home page", root_path %></div>

<div style="margin-top: 2rem;">
  <%= form_with scope: :any, url: post_something_path do |form| %>
      <%= form.submit 'Post something' %>
  <% end %>
  <turbo-frame id="messages">
    <div>An empty message</div>
  </turbo-frame>
</div>

<div style="margin-top: 2rem;">
  <h2>Stimulus</h2>  
  <div data-controller="hello">
    <input data-hello-target="name" type="text">
    <button data-action="click->hello#greet">
      Greet
    </button>
    <span data-hello-target="output">
    </span>
  </div>
</div>

Make A changeMake changes in the hello_controller.js in path app/JavaScript/controllers and add a stimulus controller in the file, which helps to bring the HTML into life.

#CODE

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "name", "output" ]

  greet() {
    this.outputTarget.textContent =
      `Hello, ${this.nameTarget.value}!`
  }
}

add a stimulus controller in the fileGo to your browser after making the changes in the code and click on Enter to other page link which will navigate to the localhost:3000/other/index page there you can see the changes implemented by the stimulus controller that is designed to augment your HTML with just enough behavior to make it more responsive.

With just a little bit of work, Turbo and Stimulus together offer a complete answer for applications that are quick and compelling.

Using Rails 7 Hotwire helps to load the pages at a faster speed and allows you to render templates on the server, where you have access to your whole domain model. It is a productive development experience in ROR, without compromising any of the speed or responsiveness associated with SPA.

Conclusion

We hope you were satisfied with our Rails Hotwire tutorial. Write to us at service@bacancy.com for any query that you want to resolve, or if you want us to share a tutorial on your query.

For more such solutions on RoR, check out our Ruby on Rails Tutorials. We will always strive to amaze you and cater to your needs.

Original article source at: https://www.bacancytechnology.com/

#rails #ruby 

Chloe  Butler

Chloe Butler

1667425440

Pdf2gerb: Perl Script Converts PDF Files to Gerber format

pdf2gerb

Perl script converts PDF files to Gerber format

Pdf2Gerb generates Gerber 274X photoplotting and Excellon drill files from PDFs of a PCB. Up to three PDFs are used: the top copper layer, the bottom copper layer (for 2-sided PCBs), and an optional silk screen layer. The PDFs can be created directly from any PDF drawing software, or a PDF print driver can be used to capture the Print output if the drawing software does not directly support output to PDF.

The general workflow is as follows:

  1. Design the PCB using your favorite CAD or drawing software.
  2. Print the top and bottom copper and top silk screen layers to a PDF file.
  3. Run Pdf2Gerb on the PDFs to create Gerber and Excellon files.
  4. Use a Gerber viewer to double-check the output against the original PCB design.
  5. Make adjustments as needed.
  6. Submit the files to a PCB manufacturer.

Please note that Pdf2Gerb does NOT perform DRC (Design Rule Checks), as these will vary according to individual PCB manufacturer conventions and capabilities. Also note that Pdf2Gerb is not perfect, so the output files must always be checked before submitting them. As of version 1.6, Pdf2Gerb supports most PCB elements, such as round and square pads, round holes, traces, SMD pads, ground planes, no-fill areas, and panelization. However, because it interprets the graphical output of a Print function, there are limitations in what it can recognize (or there may be bugs).

See docs/Pdf2Gerb.pdf for install/setup, config, usage, and other info.


pdf2gerb_cfg.pm

#Pdf2Gerb config settings:
#Put this file in same folder/directory as pdf2gerb.pl itself (global settings),
#or copy to another folder/directory with PDFs if you want PCB-specific settings.
#There is only one user of this file, so we don't need a custom package or namespace.
#NOTE: all constants defined in here will be added to main namespace.
#package pdf2gerb_cfg;

use strict; #trap undef vars (easier debug)
use warnings; #other useful info (easier debug)


##############################################################################################
#configurable settings:
#change values here instead of in main pfg2gerb.pl file

use constant WANT_COLORS => ($^O !~ m/Win/); #ANSI colors no worky on Windows? this must be set < first DebugPrint() call

#just a little warning; set realistic expectations:
#DebugPrint("${\(CYAN)}Pdf2Gerb.pl ${\(VERSION)}, $^O O/S\n${\(YELLOW)}${\(BOLD)}${\(ITALIC)}This is EXPERIMENTAL software.  \nGerber files MAY CONTAIN ERRORS.  Please CHECK them before fabrication!${\(RESET)}", 0); #if WANT_DEBUG

use constant METRIC => FALSE; #set to TRUE for metric units (only affect final numbers in output files, not internal arithmetic)
use constant APERTURE_LIMIT => 0; #34; #max #apertures to use; generate warnings if too many apertures are used (0 to not check)
use constant DRILL_FMT => '2.4'; #'2.3'; #'2.4' is the default for PCB fab; change to '2.3' for CNC

use constant WANT_DEBUG => 0; #10; #level of debug wanted; higher == more, lower == less, 0 == none
use constant GERBER_DEBUG => 0; #level of debug to include in Gerber file; DON'T USE FOR FABRICATION
use constant WANT_STREAMS => FALSE; #TRUE; #save decompressed streams to files (for debug)
use constant WANT_ALLINPUT => FALSE; #TRUE; #save entire input stream (for debug ONLY)

#DebugPrint(sprintf("${\(CYAN)}DEBUG: stdout %d, gerber %d, want streams? %d, all input? %d, O/S: $^O, Perl: $]${\(RESET)}\n", WANT_DEBUG, GERBER_DEBUG, WANT_STREAMS, WANT_ALLINPUT), 1);
#DebugPrint(sprintf("max int = %d, min int = %d\n", MAXINT, MININT), 1); 

#define standard trace and pad sizes to reduce scaling or PDF rendering errors:
#This avoids weird aperture settings and replaces them with more standardized values.
#(I'm not sure how photoplotters handle strange sizes).
#Fewer choices here gives more accurate mapping in the final Gerber files.
#units are in inches
use constant TOOL_SIZES => #add more as desired
(
#round or square pads (> 0) and drills (< 0):
    .010, -.001,  #tiny pads for SMD; dummy drill size (too small for practical use, but needed so StandardTool will use this entry)
    .031, -.014,  #used for vias
    .041, -.020,  #smallest non-filled plated hole
    .051, -.025,
    .056, -.029,  #useful for IC pins
    .070, -.033,
    .075, -.040,  #heavier leads
#    .090, -.043,  #NOTE: 600 dpi is not high enough resolution to reliably distinguish between .043" and .046", so choose 1 of the 2 here
    .100, -.046,
    .115, -.052,
    .130, -.061,
    .140, -.067,
    .150, -.079,
    .175, -.088,
    .190, -.093,
    .200, -.100,
    .220, -.110,
    .160, -.125,  #useful for mounting holes
#some additional pad sizes without holes (repeat a previous hole size if you just want the pad size):
    .090, -.040,  #want a .090 pad option, but use dummy hole size
    .065, -.040, #.065 x .065 rect pad
    .035, -.040, #.035 x .065 rect pad
#traces:
    .001,  #too thin for real traces; use only for board outlines
    .006,  #minimum real trace width; mainly used for text
    .008,  #mainly used for mid-sized text, not traces
    .010,  #minimum recommended trace width for low-current signals
    .012,
    .015,  #moderate low-voltage current
    .020,  #heavier trace for power, ground (even if a lighter one is adequate)
    .025,
    .030,  #heavy-current traces; be careful with these ones!
    .040,
    .050,
    .060,
    .080,
    .100,
    .120,
);
#Areas larger than the values below will be filled with parallel lines:
#This cuts down on the number of aperture sizes used.
#Set to 0 to always use an aperture or drill, regardless of size.
use constant { MAX_APERTURE => max((TOOL_SIZES)) + .004, MAX_DRILL => -min((TOOL_SIZES)) + .004 }; #max aperture and drill sizes (plus a little tolerance)
#DebugPrint(sprintf("using %d standard tool sizes: %s, max aper %.3f, max drill %.3f\n", scalar((TOOL_SIZES)), join(", ", (TOOL_SIZES)), MAX_APERTURE, MAX_DRILL), 1);

#NOTE: Compare the PDF to the original CAD file to check the accuracy of the PDF rendering and parsing!
#for example, the CAD software I used generated the following circles for holes:
#CAD hole size:   parsed PDF diameter:      error:
#  .014                .016                +.002
#  .020                .02267              +.00267
#  .025                .026                +.001
#  .029                .03167              +.00267
#  .033                .036                +.003
#  .040                .04267              +.00267
#This was usually ~ .002" - .003" too big compared to the hole as displayed in the CAD software.
#To compensate for PDF rendering errors (either during CAD Print function or PDF parsing logic), adjust the values below as needed.
#units are pixels; for example, a value of 2.4 at 600 dpi = .0004 inch, 2 at 600 dpi = .0033"
use constant
{
    HOLE_ADJUST => -0.004 * 600, #-2.6, #holes seemed to be slightly oversized (by .002" - .004"), so shrink them a little
    RNDPAD_ADJUST => -0.003 * 600, #-2, #-2.4, #round pads seemed to be slightly oversized, so shrink them a little
    SQRPAD_ADJUST => +0.001 * 600, #+.5, #square pads are sometimes too small by .00067, so bump them up a little
    RECTPAD_ADJUST => 0, #(pixels) rectangular pads seem to be okay? (not tested much)
    TRACE_ADJUST => 0, #(pixels) traces seemed to be okay?
    REDUCE_TOLERANCE => .001, #(inches) allow this much variation when reducing circles and rects
};

#Also, my CAD's Print function or the PDF print driver I used was a little off for circles, so define some additional adjustment values here:
#Values are added to X/Y coordinates; units are pixels; for example, a value of 1 at 600 dpi would be ~= .002 inch
use constant
{
    CIRCLE_ADJUST_MINX => 0,
    CIRCLE_ADJUST_MINY => -0.001 * 600, #-1, #circles were a little too high, so nudge them a little lower
    CIRCLE_ADJUST_MAXX => +0.001 * 600, #+1, #circles were a little too far to the left, so nudge them a little to the right
    CIRCLE_ADJUST_MAXY => 0,
    SUBST_CIRCLE_CLIPRECT => FALSE, #generate circle and substitute for clip rects (to compensate for the way some CAD software draws circles)
    WANT_CLIPRECT => TRUE, #FALSE, #AI doesn't need clip rect at all? should be on normally?
    RECT_COMPLETION => FALSE, #TRUE, #fill in 4th side of rect when 3 sides found
};

#allow .012 clearance around pads for solder mask:
#This value effectively adjusts pad sizes in the TOOL_SIZES list above (only for solder mask layers).
use constant SOLDER_MARGIN => +.012; #units are inches

#line join/cap styles:
use constant
{
    CAP_NONE => 0, #butt (none); line is exact length
    CAP_ROUND => 1, #round cap/join; line overhangs by a semi-circle at either end
    CAP_SQUARE => 2, #square cap/join; line overhangs by a half square on either end
    CAP_OVERRIDE => FALSE, #cap style overrides drawing logic
};
    
#number of elements in each shape type:
use constant
{
    RECT_SHAPELEN => 6, #x0, y0, x1, y1, count, "rect" (start, end corners)
    LINE_SHAPELEN => 6, #x0, y0, x1, y1, count, "line" (line seg)
    CURVE_SHAPELEN => 10, #xstart, ystart, x0, y0, x1, y1, xend, yend, count, "curve" (bezier 2 points)
    CIRCLE_SHAPELEN => 5, #x, y, 5, count, "circle" (center + radius)
};
#const my %SHAPELEN =
#Readonly my %SHAPELEN =>
our %SHAPELEN =
(
    rect => RECT_SHAPELEN,
    line => LINE_SHAPELEN,
    curve => CURVE_SHAPELEN,
    circle => CIRCLE_SHAPELEN,
);

#panelization:
#This will repeat the entire body the number of times indicated along the X or Y axes (files grow accordingly).
#Display elements that overhang PCB boundary can be squashed or left as-is (typically text or other silk screen markings).
#Set "overhangs" TRUE to allow overhangs, FALSE to truncate them.
#xpad and ypad allow margins to be added around outer edge of panelized PCB.
use constant PANELIZE => {'x' => 1, 'y' => 1, 'xpad' => 0, 'ypad' => 0, 'overhangs' => TRUE}; #number of times to repeat in X and Y directions

# Set this to 1 if you need TurboCAD support.
#$turboCAD = FALSE; #is this still needed as an option?

#CIRCAD pad generation uses an appropriate aperture, then moves it (stroke) "a little" - we use this to find pads and distinguish them from PCB holes. 
use constant PAD_STROKE => 0.3; #0.0005 * 600; #units are pixels
#convert very short traces to pads or holes:
use constant TRACE_MINLEN => .001; #units are inches
#use constant ALWAYS_XY => TRUE; #FALSE; #force XY even if X or Y doesn't change; NOTE: needs to be TRUE for all pads to show in FlatCAM and ViewPlot
use constant REMOVE_POLARITY => FALSE; #TRUE; #set to remove subtractive (negative) polarity; NOTE: must be FALSE for ground planes

#PDF uses "points", each point = 1/72 inch
#combined with a PDF scale factor of .12, this gives 600 dpi resolution (1/72 * .12 = 600 dpi)
use constant INCHES_PER_POINT => 1/72; #0.0138888889; #multiply point-size by this to get inches

# The precision used when computing a bezier curve. Higher numbers are more precise but slower (and generate larger files).
#$bezierPrecision = 100;
use constant BEZIER_PRECISION => 36; #100; #use const; reduced for faster rendering (mainly used for silk screen and thermal pads)

# Ground planes and silk screen or larger copper rectangles or circles are filled line-by-line using this resolution.
use constant FILL_WIDTH => .01; #fill at most 0.01 inch at a time

# The max number of characters to read into memory
use constant MAX_BYTES => 10 * M; #bumped up to 10 MB, use const

use constant DUP_DRILL1 => TRUE; #FALSE; #kludge: ViewPlot doesn't load drill files that are too small so duplicate first tool

my $runtime = time(); #Time::HiRes::gettimeofday(); #measure my execution time

print STDERR "Loaded config settings from '${\(__FILE__)}'.\n";
1; #last value must be truthful to indicate successful load


#############################################################################################
#junk/experiment:

#use Package::Constants;
#use Exporter qw(import); #https://perldoc.perl.org/Exporter.html

#my $caller = "pdf2gerb::";

#sub cfg
#{
#    my $proto = shift;
#    my $class = ref($proto) || $proto;
#    my $settings =
#    {
#        $WANT_DEBUG => 990, #10; #level of debug wanted; higher == more, lower == less, 0 == none
#    };
#    bless($settings, $class);
#    return $settings;
#}

#use constant HELLO => "hi there2"; #"main::HELLO" => "hi there";
#use constant GOODBYE => 14; #"main::GOODBYE" => 12;

#print STDERR "read cfg file\n";

#our @EXPORT_OK = Package::Constants->list(__PACKAGE__); #https://www.perlmonks.org/?node_id=1072691; NOTE: "_OK" skips short/common names

#print STDERR scalar(@EXPORT_OK) . " consts exported:\n";
#foreach(@EXPORT_OK) { print STDERR "$_\n"; }
#my $val = main::thing("xyz");
#print STDERR "caller gave me $val\n";
#foreach my $arg (@ARGV) { print STDERR "arg $arg\n"; }

Download Details:

Author: swannman
Source Code: https://github.com/swannman/pdf2gerb

License: GPL-3.0 license

#perl 

Implementing a Rock-Paper-Scissors Game Using Event Sourcing

In this tutorial, we will look at how we can design the game flow for a rock-paper-scissors game using Serialized APIs for Event Sourcing and CQRS.

Our favorite runtime environment for applications is usually Dropwizard but since many out there prefer Spring Boot I decided to use it instead for this article. Serialized Java client works with any runtime environment or platform you use on the JVM.

Configure the Serialized project

To develop our game we will use Serialized aggregates and projections. The aggregates will store the events for each game and the projections will provide a view of each game as well as a high score list of the top winners (in the case of multiple games being run).

If you have not yet signed up to Serialized you will need to sign up for a free developer account. Once you’ve signed up and created your first project you will have an empty view of Aggregates, like this:

Empty View of Aggregates

We now need to find out API keys that are available under Settings.

Finding API Keys

Copy the access key and secret access key to a safe location. We will need these to access Serialized APIs from our backend application.

Great job! We now have an empty Serialized project. We’re now ready to start developing our game!

#java #spring boot #event sourcing #cqrs #implementing a rock-paper-scissors game using event sourcing #rock-paper-scissors game

Bongani  Ngema

Bongani Ngema

1669293240

How to Code A Rock Paper Scissors Game using JavaScript

Coding a rock paper scissors game with JavaScript, HTML, and CSS

This tutorial will help you learn how to code a rock paper scissors game using JavaScript as its main engine.

The end result of this tutorial is as follows:

Screenshot of rock paper scissors game with JavaScriptScreenshot of rock paper scissors game with JavaScript

The complete code is stored in the following GitHub repository. You can play the game on this GitHub Page.

Let’s start with creating the core game mechanism using JavaScript and HTML first.

Rock paper scissors game using JavaScript and HTML

To create the code mechanics of the rock, paper, and scissors game, you need to create three HTML <button> elements with each represents an option for the user.

You can create them as follows:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
</body>

Next, you need to grab the elements using JavaScript and add an event listener to each one of them. The querySelectorAll() method is used because the returned NodeList object implements the forEach() method.

Take a look at the code below:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
  <script>
    const options = document.querySelectorAll(".options");

    options.forEach((option) => {
      option.addEventListener("click", function () {
        // TO DO: process user click
      });
    });
  </script>
</body>

The callback function passed into addEventListener() method will be executed when the user clicks on one of the three buttons.

When a click event is triggered, you need to fetch the user’s selected option by reading the value of the textContent property. Let’s store the value in a variable called pInput which stands for Player Input:

const pInput = this.textContent;

The this keyword in the code above refers to the <button> element that was clicked by the user. If the user clicked on "Rock" button, then pInput variable value will be of "Rock" string.

Once you have the pInput, it’s time to get the computer selection. The computer needs to select one of the three available choices.

You can use the Math.random() method to generate a random number between 0-2 and use it to randomly select between rock, paper, or scissors for the computer.

Here’s how you do it:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
  <script>
    const options = document.querySelectorAll(".options");

    options.forEach((option) => {
      option.addEventListener("click", function () {
        const pInput = this.textContent;

        const cOptions = ["Rock", "Paper", "Scissors"];
        const cInput = cOptions[Math.floor(Math.random() * 3)];
      });
    });
  </script>
</body>

The cInput variable stands for Computer Input.

Now that you have both pInput and cInput ready, it’s time to create a function to compare them and find out who wins the current round.

Let’s call the function compareInputs() and start by creating an if statement that ends the round with a tie:

function compareInputs(pInput, cInput) {
  const currentMatch = `${pInput} vs ${cInput}`;

  // Tie check
  if (pInput === cInput) {
    alert(`${currentMatch} is a Tie`);
    return;
  }
}

When the inputs are not equal, you can start checking pInput value if it matches one of the three possible options. The code below starts with checking if the user selects "Rock" for the current round:

function compareInputs(pInput, cInput) {
  const currentMatch = `${pInput} vs ${cInput}`;

  // Tie check
  if (pInput === cInput) {
    alert(`${currentMatch} is a Tie`);
    return;
  }

  // Rock
  if (pInput === "Rock") {
    if (cInput === "Scissors") {
      alert(`${currentMatch} = You Win`);
    } else {
      alert(`${currentMatch} = Computer Wins`);
    }
  }
}

Since you’ve checked for the possibility of a tie first, you only need to create one if..else block for the outcome.

After that, you need to check for both "Paper" and "Scissors" as follows:

function compareInputs(pInput, cInput) {
  const currentMatch = `${pInput} vs ${cInput}`;

  // Tie check
  if (pInput === cInput) {
    alert(`${currentMatch} is a Tie`);
    return;
  }

  // Rock
  if (pInput === "Rock") {
    if (cInput === "Scissors") {
      alert(`${currentMatch} = You Win`);
    } else {
      alert(`${currentMatch} = Computer Wins`);
    }
  }
  // Paper
  else if (pInput === "Paper") {
    if (cInput === "Rock") {
      alert(`${currentMatch} = You Win`);
    } else {
      alert(`${currentMatch} = Computer Wins`);
    }
  }
  // Scissors
  else {
    if (cInput === "Paper") {
      alert(`${currentMatch} = You Win`);
    } else {
      alert(`${currentMatch} = Computer Wins`);
    }
  }
}

With the function completed, you just need to call it inside the addEventListener() method:

option.addEventListener("click", function () {
  const pInput = this.textContent;

  const cOptions = ["Rock", "Paper", "Scissors"];
  const cInput = cOptions[Math.floor(Math.random() * 3)];

  compareInputs(pInput, cInput);
});

And with that, you have completed the core mechanics of the rock paper scissors game.

The full code is as shown below:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
  <script>
    const options = document.querySelectorAll(".options");

    options.forEach((option) => {
      option.addEventListener("click", function () {
        const pInput = this.textContent;

        const cOptions = ["Rock", "Paper", "Scissors"];
        const cInput = cOptions[Math.floor(Math.random() * 3)];

        compareInputs(pInput, cInput);
      });
    });

    function compareInputs(pInput, cInput) {
      const currentMatch = `${pInput} vs ${cInput}`;

      // Tie check
      if (pInput === cInput) {
        alert(`${currentMatch} is a Tie`);
        return;
      }

      // Rock
      if (pInput === "Rock") {
        if (cInput === "Scissors") {
          alert(`${currentMatch} = You Win`);
        } else {
          alert(`${currentMatch} = Computer Wins`);
        }
      }
      // Paper
      else if (pInput === "Paper") {
        if (cInput === "Rock") {
          alert(`${currentMatch} = You Win`);
        } else {
          alert(`${currentMatch} = Computer Wins`);
        }
      }
      // Scissors
      else if (pInput === "Scissors") {
        if (cInput === "Paper") {
          alert(`${currentMatch} = You Win`);
        } else {
          alert(`${currentMatch} = Computer Wins`);
        }
      }
    }
  </script>
</body>

Now that you’ve learned the basic mechanics of the rock paper scissors game, it’s time to add more features to improve the gaming experience.

Improving the gaming experience for rock paper scissors game

With the core game mechanism set, you can start improving the game experience by keeping scores. For example, once the user or the computer gains five wins, the winner is called out and the score resets back to zero.

This can be done by creating two variables that keep the scores for both the user and the computer. The variable values will be updated when the user or the computer wins, and another function will check if either variable is equal to five.

Let’s start by updating the HTML with new elements to display the game objective and the scores:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <h2>Win five rounds to be the victor</h2>
  <h3>
    Player <span id="p-score">0</span> - <span id="c-score">0</span> Computer
  </h3>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
</body>

The <span> elements above will be updated with the new variables named pScore and cScore. Let’s write the new variables inside the <script> tag:

const options = document.querySelectorAll(".options");
let pScore = 0;
let cScore = 0;

options.forEach((option) => {
  // ...
});

Next, increment the pScore value by one when the user wins, or the cScore value when the computer wins:

if (pInput === "Rock") {
  if (cInput === "Scissors") {
    alert(`${currentMatch} = You Win`);
    pScore++;
  } else {
    alert(`${currentMatch} = Computer Wins`);
    cScore++;
  }
}
//Check for Paper
else if (pInput === "Paper") {
  if (cInput === "Rock") {
    alert(`${currentMatch} = You Win`);
    pScore++;
  } else {
    alert(`${currentMatch} = Computer Wins`);
    cScore++;
  }
}
//Check for Scissors
else {
  if (cInput === "Paper") {
    alert(`${currentMatch} = You Win`);
    pScore++;
  } else {
    alert(`${currentMatch} = Computer Wins`);
    cScore++;
  }
}

Once the scores are updated, call a new function to update the scores displayed in the browser. Let’s call the function updateScore() method:

options.forEach((option) => {
  option.addEventListener("click", function () {
    const pInput = this.textContent;

    const computerOptions = ["Rock", "Paper", "Scissors"];
    const cInput = computerOptions[Math.floor(Math.random() * 3)];

    compareInputs(pInput, cInput);
    updateScore();
  });
});

The code inside the updateScore() method will update the <span> elements you’ve added before:

function updateScore() {
  document.getElementById("pScore").textContent = pScore;
  document.getElementById("cScore").textContent = cScore;
}

Finally, you need another function to check if one of the players already gained five wins. Let’s create another function named checkWinner() to do just that:

function checkWinner() {
  if (pScore === 5 || cScore === 5) {
    const winner =
      pScore === 5
        ? "You win the game! Congratulations!"
        : "Computer wins the game! Try again next time!";
    alert(winner);
    return true;
  }
  return false;
}

Call the checkWinner() function right above the call to updateScore() function. When it returns true, set the scores back to zero and call updateScore() again to refresh the rendered scores:

compareInputs(pInput, cInput);
updateScore();
if (checkWinner()) {
  pScore = cScore = 0;
  updateScore();
}

Now your rock paper scissors game will keep the scores and declare the winner when 5 wins have been gained.

Here’s the complete code again:

<body>
  <h1>Basic Rock Paper Scissors game with JavaScript</h1>
  <h2>Win five rounds to be the victor</h2>
  <h3>
    Player <span id="p-score">0</span> - <span id="c-score">0</span> Computer
  </h3>
  <button class="options">Rock</button>
  <button class="options">Paper</button>
  <button class="options">Scissors</button>
  <script>
    const options = document.querySelectorAll(".options");
    let pScore = 0;
    let cScore = 0;

    options.forEach((option) => {
      option.addEventListener("click", function () {
        const pInput = this.textContent;

        const cOptions = ["Rock", "Paper", "Scissors"];
        const cInput = cOptions[Math.floor(Math.random() * 3)];

        compareInputs(pInput, cInput);
        updateScore();
        if (checkWinner()) {
          pScore = cScore = 0;
          updateScore();
        }
      });
    });

    function compareInputs(pInput, cInput) {
      const currentMatch = `${pInput} vs ${cInput}`;
      if (pInput === cInput) {
        alert(`${currentMatch} is a Tie`);
        return;
      }

      if (pInput === "Rock") {
        if (cInput === "Scissors") {
          alert(`${currentMatch} = You Win`);
          pScore++;
        } else {
          alert(`${currentMatch} = Computer Wins`);
          cScore++;
        }
      }
      //Check for Paper
      else if (pInput === "Paper") {
        if (cInput === "Rock") {
          alert(`${currentMatch} = You Win`);
          pScore++;
        } else {
          alert(`${currentMatch} = Computer Wins`);
          cScore++;
        }
      }
      //Check for Scissors
      else {
        if (cInput === "Paper") {
          alert(`${currentMatch} = You Win`);
          pScore++;
        } else {
          alert(`${currentMatch} = Computer Wins`);
          cScore++;
        }
      }
    }

    function updateScore() {
      document.getElementById("p-score").textContent = pScore;
      document.getElementById("c-score").textContent = cScore;
    }

    function checkWinner() {
      if (pScore === 5 || cScore === 5) {
        const winner =
          pScore === 5
            ? "You win the game! Congratulations!"
            : "Computer wins the game! Try again next time!";
        alert(winner);
        return true;
      }
      return false;
    }
  </script>
</body>

With that, your rock paper scissors game mechanic is completed.

Up until now, you haven’t touched the user interface of your game, so let’s add some style and make the interface prettier in the next section.

Styling the rock paper scissors game

You can make the game interface better by using CSS to add colors and borders. But the most important improvement would be to replace the <button> elements with images of rock, paper, and scissors.

First, you need to download the images for the actions here.

Note: the images are created by Cristiano Zoucas from Noun Project free of charge

Once you have the buttons, update your HTML <body> tag as shown below:

<body>
  <div class="container">
    <h1>The Rock Paper Scissors Game</h1>
    <h2>Win five rounds to be the victor</h2>
    <h2>
      Player <span id="p-score">0</span> - <span id="c-score">0</span> Computer
    </h2>
    <div class="display-moves">
      <div class="move">
        <img id="p-move" src="./assets/Rock.svg">
      </div>
      <div class="separator">VS</div>
      <div class="move">
        <img id="c-move" src="./assets/Rock.svg">
      </div>
    </div>
    <h2>Click on the image below to select your move:<h2>
    <div class="action">
      <input class="options" type="image" src="./assets/Rock.svg" value="Rock" />
      <input class="options" type="image" src="./assets/Paper.svg" value="Paper" />
      <input class="options" type="image" src="./assets/Scissors.svg" value="Scissors" />
    </div>
  </div>
</body>

The <button> elements are replaced with the <input> elements using the type image. Additional <div> elements are added to the HTML page to help with CSS styling.

The CSS code for the page will be as follows:

body {
  text-align: center;
}
.container {
  margin: 0 auto;
  background-color: aquamarine;
  border-radius: 20px;
  padding: 25px 50px;
  width: 80vw;
}
.action {
  max-width: 800px;
  padding: 40px 0;
  margin: 0 auto;
  background-color: cadetblue;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-evenly;
  border-radius: 20px;
}
.options {
  flex: 1 1 30%;
  border-radius: 50%;
  border-width: 4px;
  border-style: solid;
  border-color: rgba(0, 0, 0, 0.7);
  padding: 10px;
  animation-name: anim-fade;
  animation-duration: 1s;
  animation-fill-mode: forwards;
  width: 100%;
  max-width: 120px;
}
.display-moves {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-evenly;
}
.move {
  border-radius: 50%;
  border-width: 4px;
  border-style: solid;
  border-color: rgba(0, 0, 0, 0.7);
}
.separator {
  font-size: 5em;
}

The CSS above will add background color to the HTML page and create a circle border around the images. The last improvement you need to do is to update the JavaScript code.

First, revise the pInput variable value to fetch from value property instead of textContent:

const pInput = this.value;

After that, create a function to update the player and the computer actions (the smaller images)

Let’s name it the updateMoves() function:

function updateMoves(pInput, cInput) {
  document.getElementById("p-move").src = `./assets/${pInput}.svg`;
  document.getElementById("c-move").src = `./assets/${cInput}.svg`;
}

Finally, call the updateMoves() right before calling the compareInputs() function:

option.addEventListener("click", function () {
  const pInput = this.value;

  const cOptions = ["Rock", "Paper", "Scissors"];
  const cInput = cOptions[Math.floor(Math.random() * 3)];

  updateMoves(pInput, cInput);
  compareInputs(pInput, cInput);
  updateScore();
  if (checkWinner()) {
    pScore = cScore = 0;
    updateScore();
  }
});

Now your rock paper scissors game looks a lot better than before. Here’s a GitHub repository storing the finished game.

Conclusion

You’ve learned how to code a rock paper scissors game using JavaScript, HTML, and CSS.

Optionally, you can still improve the application further by adding an option for users to select how many rounds they want to play the game.

But don’t worry about it and congratulations on making it this far! 😉

Original article source at: https://sebhastian.com/

#javascript #game 

Tyrique  Littel

Tyrique Littel

1604008800

Static Code Analysis: What It Is? How to Use It?

Static code analysis refers to the technique of approximating the runtime behavior of a program. In other words, it is the process of predicting the output of a program without actually executing it.

Lately, however, the term “Static Code Analysis” is more commonly used to refer to one of the applications of this technique rather than the technique itself — program comprehension — understanding the program and detecting issues in it (anything from syntax errors to type mismatches, performance hogs likely bugs, security loopholes, etc.). This is the usage we’d be referring to throughout this post.

“The refinement of techniques for the prompt discovery of error serves as well as any other as a hallmark of what we mean by science.”

  • J. Robert Oppenheimer

Outline

We cover a lot of ground in this post. The aim is to build an understanding of static code analysis and to equip you with the basic theory, and the right tools so that you can write analyzers on your own.

We start our journey with laying down the essential parts of the pipeline which a compiler follows to understand what a piece of code does. We learn where to tap points in this pipeline to plug in our analyzers and extract meaningful information. In the latter half, we get our feet wet, and write four such static analyzers, completely from scratch, in Python.

Note that although the ideas here are discussed in light of Python, static code analyzers across all programming languages are carved out along similar lines. We chose Python because of the availability of an easy to use ast module, and wide adoption of the language itself.

How does it all work?

Before a computer can finally “understand” and execute a piece of code, it goes through a series of complicated transformations:

static analysis workflow

As you can see in the diagram (go ahead, zoom it!), the static analyzers feed on the output of these stages. To be able to better understand the static analysis techniques, let’s look at each of these steps in some more detail:

Scanning

The first thing that a compiler does when trying to understand a piece of code is to break it down into smaller chunks, also known as tokens. Tokens are akin to what words are in a language.

A token might consist of either a single character, like (, or literals (like integers, strings, e.g., 7Bob, etc.), or reserved keywords of that language (e.g, def in Python). Characters which do not contribute towards the semantics of a program, like trailing whitespace, comments, etc. are often discarded by the scanner.

Python provides the tokenize module in its standard library to let you play around with tokens:

Python

1

import io

2

import tokenize

3

4

code = b"color = input('Enter your favourite color: ')"

5

6

for token in tokenize.tokenize(io.BytesIO(code).readline):

7

    print(token)

Python

1

TokenInfo(type=62 (ENCODING),  string='utf-8')

2

TokenInfo(type=1  (NAME),      string='color')

3

TokenInfo(type=54 (OP),        string='=')

4

TokenInfo(type=1  (NAME),      string='input')

5

TokenInfo(type=54 (OP),        string='(')

6

TokenInfo(type=3  (STRING),    string="'Enter your favourite color: '")

7

TokenInfo(type=54 (OP),        string=')')

8

TokenInfo(type=4  (NEWLINE),   string='')

9

TokenInfo(type=0  (ENDMARKER), string='')

(Note that for the sake of readability, I’ve omitted a few columns from the result above — metadata like starting index, ending index, a copy of the line on which a token occurs, etc.)

#code quality #code review #static analysis #static code analysis #code analysis #static analysis tools #code review tips #static code analyzer #static code analysis tool #static analyzer