• Author: Chen Da Yu Tou
  • Making: KRISACHAN

preface

I don’t know if you played a game called tic-Tac-toe when you were a kid.

It looks like this:

(I win, praise me.)

Above is the final result of this article, an AI tic-tac-toe game implemented with pure CSS, Mmmm, although it seems a little silly…

Here is the address:

Codepen. IO/krischan77 /…

The rules of the game is relatively simple, that is, in a nine grids (it is said that 16 grids, 25 grids also line ~ anyway is a grid line), as long as you can play a straight line, even if you win.

So this time fish head will teach you how to win in this game.

Oh, no, fog

How to implement the above game with pure CSS

The body of the

Julio cruz choice

From the GIF at the beginning, we can see that this game is actually hand-selected.

We can choose whether the player goes first or the computer goes first.

So how do you do this with a simple HTML tag + CSS attribute?

It has the following effects:

HMM, so let’s use JS simulation instead!

What about CSS? Give me a)

Sorry, we can simulate this with the

The

The source code is as follows:

<style>
    .switch {
        display: inline-block;
        width: 48px;
        height: 24px;
        background: #c4d7d6;
        vertical-align: bottom;
        margin: 0 10px;
        border-radius: 16px;
        position: relative;
        cursor: pointer;
    }
    .switch::before {
        content: ' ';
        position: absolute;
        display: block;
        width: 16px;
        height: 16px;
        top: 4px;
        left: 4px;
        background: #2e317c;
        border-radius: 100%;
        transition: all 0.25 s;
    }
    #switch:checked ~ label[for='switch']::before {
        left: 28px;
        background: # 863020;
    }
</style>
checkbox: <input type="checkbox" id="switch" />
<label for="switch" class="switch"></label>
Copy the code

Then we look at Figure 1, we can see that when we select, we can control the “computer go” button.

So how does that work?

CSS doesn’t work, so let’s use JS.

(Melon eaters: ??????)

Fall, fall, fall fall. CSS can also be implemented!

We see the selector **~** in the source code above.

This is called “sibling selector”, and you can select siblings in the same order, no matter how far apart, always heart to heart.

For example, have the following HTML structure:

<span>This is not red.</span>
<p>Here is a paragraph.</p>
<code>Here is some code.</code>
<span>And here is a span.</span>
Copy the code

The following CSS:

p ~ span {
  color: red;
}
Copy the code

You can also select after .

So we have:

The code is as follows:

<style>
    #computer {
        width: 100px;
        display: inline-block;
        background: # 131824;
        color: #eef7f2;
        border-radius: 5px;
        margin-top: 10px;
        padding: 5px;
        box-sizing: border-box;
        cursor: pointer;
        transition: all 0.25 s;
    }
    #switch ~ #computer {
        display: none;
    }
    #switch:checked ~ #computer {
        display: block;
    }
</style>
checkbox: <input type="checkbox" id="switch" />
<label for="switch" class="switch"></label>
<div id="computer" class="computer">The computer!</div>
Copy the code

And after that?

Looking back at Figure 1, the option to select a first hand appears in the form of a popover, just to make sure you don’t contaminate the board before selecting a first hand. So how do you do that?

From the DEMO above, we found that there is a :checked selector, which indicates the checked status of any optional element, such as , < Input Type =”checkbox”>, and

So we have the following effects:

The code is as follows:

<style>
    .switch {
        display: inline-block;
        width: 48px;
        height: 24px;
        background: #c4d7d6;
        vertical-align: bottom;
        margin: 0 10px;
        border-radius: 16px;
        position: relative;
        cursor: pointer;
    }
    .switch::before {
        content: ' ';
        position: absolute;
        display: block;
        width: 16px;
        height: 16px;
        top: 4px;
        left: 4px;
        background: #2e317c;
        border-radius: 100%;
        transition: all 0.25 s;
    }
    #switch:checked ~ label[for='switch']::before {
        left: 28px;
        background: # 863020;
    }
    .btn {
        width: auto;
        display: inline-block;
        background: # 131824;
        color: #eef7f2;
        border-radius: 5px;
        margin-top: 10px;
        padding: 5px;
        box-sizing: border-box;
        cursor: pointer;
        transition: all 0.25 s;
    }
    #switch ~ #computer {
        display: none;
    }
    #switch:checked ~ #computer {
        display: inline-block;
    }
    #start:checked ~ .container {
        display: none;
    }
</style>
<input type="radio" id="start" />
checkbox: <input type="checkbox" id="switch" />
<div class="container">
    <br />
    <label for="switch" class="switch"></label>
    <br />
    <br />
    <label for="start" class="btn">Mantis Shrimp, let's move</label>
</div>
<div id="computer" class="btn">The computer!</div>
Copy the code

Let’s draw the board

Next we are to draw the checkerboard, in fact the checkerboard is a more conventional nine grid, can be realized in many ways, but this time fish head to amli girD layout online generated website: grid.malven.co/

The DEMO layout in Figure 1 was generated using this tool, which is very handy

The chessboard is ready. Where are the pieces?

Okay, we’ve drawn the board. What about the pieces?

Well, you can go to a stationery store and buy a box of black and white for 15 bucks, and then you can play.

The fog ~

With the board we should draw chess pieces, chess pieces how to draw?

In fact, it doesn’t matter how you draw it, the important thing is to make sure that each grid can play two pieces.

Before we draw the pieces let’s talk about state management.

The as a replaceable element is a real gem, and has become more powerful because of it and subsequent improvements in browsers.

Based on our previous development experience and the above description, we can easily relate two properties that store positive and negative states: and .

The of these two different attributes can store selection state.

The only difference is that the selection state itself is one-way and irreversible, and can only be toggled by the associated .

is reversible, and the state change can be done only with the current tag. The effect is as follows:

So let’s go back to tic-Tac-toe.

Each cell on our board will have three states, one for the beginning, one for our position, and one for the computer position.

If expressed in numbers, then:

Status code meaning
00 No children
01 We move later
10 Computer move later

So the idea is to put two on each grid, and use one of the selected tags to determine the style of rendering within the pieces. The style of chess pieces can be beautified with their own, according to our needs to draw

So the HTML for our board looks like this:

<form id="container" class="container">
    <input type="radio" name="c-radio-0" id="c-radio-0-X" />
    <input type="radio" name="c-radio-0" id="c-radio-0-O" />.<div id="c-board" class="c-center">
        <div class="c-grid" id="c-grid-0">
            <label for="c-radio-0-X"></label>
            <div></div>
        </div>.</div>
    <div id="c-computer" class="c-btn">The computer!<label for="c-radio-0-O"></label>.</div>
Copy the code

Now that the basic board layout is complete, it’s time to deal with the hand rule.

Come on, hurt each other

So let’s analyze the subroutine step by step.

First we come to kangkang tool man tag:

<div class="c-grid" id="c-grid-0">
    <label for="c-radio-0-X"></label>
    <div></div>
</div>
Copy the code

You don’t have to look at the tag, like a penny-pinching dog, but when you need it, it can become a very useful tool man.

The purpose of this tag is to carry the tag of the child.

For example, if we define our tag id as input[id*=’ -numbered -x ‘] and computer id as input[id*=’ -numbered -0′], then we can use the **~** selector to determine the tool’s rendering style, for example:

input[id*='-0-X']:checked~#c-board #c-grid-0 div::before {
    content: 'X';
    background: var(--color1);
    color: var(--color3);
}

input[id*='-0-O']:checked~#c-board #c-grid-0 div::before {
    content: 'O';
    background: var(--color2);
    color: var(--color3);
}
Copy the code

It is important to note that the input[id] of each grid is the presence of both O and X, not the same one. This is to ensure that the state is irreversible, and when checked, it is not allowed to recover.

Yeah, that’s it.

Now that we’ve determined how to render the drop, it’s time to figure out how to drop the drop.

We know that we can render input[id*=’ -0-x ‘] and input[id*=’ -0-o ‘] in a grid. We can also click to determine which one to render, but how do we determine which one to click?

So let’s get this straight.

First of all, we play chess, it doesn’t matter, like small X king learning machine, where don’t understand where can, so easy~

But the computer side is controlled by the computer, in this DEMO, you need to click the “computer Go” button below to make it automatically, so you need to hide it at first.

#c-computer { display: none; }
Copy the code

In addition, after we finish the game, this button needs to appear, after pressing it needs to be hidden, so we just need to alternate it to show, that is:

#c-computer.input:checked~input:checked~#c-computer.input:checked~input:checked~input:checked~input:checked~#c-computer.input:checked~input:checked~input:checked~input:checked~input:checked~input:checked~#c-computer.input:checked~input:checked~input:checked~input:checked~input:checked~input:checked~input:checked~input:checked~#c-computer {
	display: none;
}

input:checked~#c-computer.input:checked~input:checked~input:checked~#c-computer.input:checked~input:checked~input:checked~input:checked~input:checked~#c-computer.input:checked~input:checked~input:checked~input:checked~input:checked~input:checked~input:checked~#c-computer {
	display: block;
}
Copy the code

I’m going to display: block after the first :checked , and display: None, and so on, and so on, and so on, and so on, and so on.

Computer location

Our location can be determined by our active click, how about the computer side?

After all, it would be awkward if we had to determine the location of the target.

First, let’s take a look at the computer-related HTML structure.

<div id="c-computer" class="c-btn">The computer!<label for="c-radio-0-O"></label>
    <label for="c-radio-1-O"></label>
    <label for="c-radio-2-O"></label>
    <label for="c-radio-3-O"></label>
    <label for="c-radio-4-O"></label>
    <label for="c-radio-5-O"></label>
    <label for="c-radio-6-O"></label>
    <label for="c-radio-7-O"></label>
    <label for="c-radio-8-O"></label>
</div>
Copy the code

From the above, we can see that when we click the “computer go” button, we actually click the label[for$=’ -o ‘].

[for$=’ -x ‘] [for$=’ -x ‘]

Now that we’re talking about “hierarchy” **, it’s not hard to imagine that we can use z-index to determine which label is clicked.

Let’s look at the real chestnut.

So we can control where the computer hits each time.

How do you know for sure?

We can determine this based on the placement of ** “player” **.

For example, if the player has a :checked at position 0, then we can determine the location of the “computer” as we wish, and so on.

For example:

#c-radio-0-X:checked~#c-radio-4-X:checked~#c-radio-8-O:checked~#c-computer label[for='c-radio-2-O']. {z-index: 2;
}

#c-radio-0-O:not(:checked) ~#c-radio-2-O:not(:checked) ~#c-radio-4-X:checked~#c-radio-6-O:not(:checked) ~#c-radio-8-O:not(:checked) ~#c-computer label[for='c-radio-0-O']. {z-index: 2;
}
Copy the code

Winning or losing judgment

All right, finally, it’s time for our last segment, which is how to judge a winner.

This part is determined by the placement of the two pieces.

As we all know, we have the following ways to win:

The letter ** “X” ** represents the winning rule:

<! -- XXX OOO OOO XOO OXO OOX XOO OOX OOO XXX OOO XOO OXO OOX OXO OXO OOO OOO xxx XOO OXO OOX OOX XOO -->
Copy the code

There should be no missing, just the above, so we just need to judge whether the two sides meet the above rules, so we have:

#c-radio-0-X:checked~#c-radio-1-X:checked~#c-radio-2-X:checked~#c-result #c-info::before.#c-radio-3-X:checked~#c-radio-4-X:checked~#c-radio-5-X:checked~#c-result #c-info::before.#c-radio-6-X:checked~#c-radio-7-X:checked~#c-radio-8-X:checked~#c-result #c-info::before.#c-radio-0-X:checked~#c-radio-3-X:checked~#c-radio-6-X:checked~#c-result #c-info::before.#c-radio-1-X:checked~#c-radio-4-X:checked~#c-radio-7-X:checked~#c-result #c-info::before.#c-radio-2-X:checked~#c-radio-5-X:checked~#c-radio-8-X:checked~#c-result #c-info::before.#c-radio-0-X:checked~#c-radio-4-X:checked~#c-radio-8-X:checked~#c-result #c-info::before.#c-radio-2-X:checked~#c-radio-4-X:checked~#c-radio-6-X:checked~#c-result #c-info::before {
    content: 'Congratulations on winning.';
}

#c-radio-0-O:checked~#c-radio-1-O:checked~#c-radio-2-O:checked~#c-result #c-info::before.#c-radio-3-O:checked~#c-radio-4-O:checked~#c-radio-5-O:checked~#c-result #c-info::before.#c-radio-6-O:checked~#c-radio-7-O:checked~#c-radio-8-O:checked~#c-result #c-info::before.#c-radio-0-O:checked~#c-radio-3-O:checked~#c-radio-6-O:checked~#c-result #c-info::before.#c-radio-1-O:checked~#c-radio-4-O:checked~#c-radio-7-O:checked~#c-result #c-info::before.#c-radio-2-O:checked~#c-radio-5-O:checked~#c-radio-8-O:checked~#c-result #c-info::before.#c-radio-0-O:checked~#c-radio-4-O:checked~#c-radio-8-O:checked~#c-result #c-info::before.#c-radio-2-O:checked~#c-radio-4-O:checked~#c-radio-6-O:checked~#c-result #c-info::before {
    content: 'Too bad you lost.';
}
Copy the code

(eat melon masses: “perfect head, if did not lose did not win?” )

What if I don’t lose, I don’t lose, I don’t win, I don’t lose, I don’t win? No way, use JS…

Sorry, I was wrong, this function only needs to give the prompt tag a default text.

Of course we have to write a logic for the popover to pop up.

input:checked~input:checked~input:checked~input:checked~input:checked~input:checked~input:checked~input:checked~input:checked~#c-result. {display: block;
}
Copy the code

When all the Spaces are checked and a few key Spaces are filled, let it show.

Initialize the

What if we want to play the next game?

Refresh the page!!

(Melon eating crowd: “here?” )

is a button that initializes all in the form with one click, like this

One key initialization, very convenient ~

conclusion

is a very useful and interesting alternative tag that is used by most pure CSS games in the industry. It’s not particularly useful, but combined with selectors, it can help us solve a lot of problems in our business.

The resources

  1. Pure CSS Tic-Tac-toe: the unmysterious JOURNEY of CSS AI programming

Afterword.

If you like to discuss technology, or have any comments or suggestions on this article, you are welcome to add yu Tou wechat friends to discuss. Of course, Yu Tou also hopes to talk about life, hobbies and things with you. His wechat id is krisChans95