본문 바로가기

매일 TIL

[내일배움캠프 16일차] 가위바위보 게임

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>rsp_select</title>
    <style>
        #user {
            height: 274px;
        }

        canvas {
            pointer-events: none;
            position: fixed;
            top: 0;
            transform: scale(1.1);
        }
    </style>
</head>

<body>

    <h1 class="text-center">가위바위보 게임을 해봅시다!</h1>

    <button id="startButton" style="display:none"></button>
    <canvas id="canvas" style="display:none"></canvas>

    <div class="container text-center">
        <div>
            <button type="button" class="btn btn-outline-secondary border-0 border-height-100" id="user" name="user"
                value="가위" data-bs-toggle="modal" data-bs-target="#resultModal" onclick="RPSSelecter('가위')">
            </button>
            <button type="button" class="btn btn-outline-secondary border-0" id="user" name="user" value="바위"
                data-bs-toggle="modal" data-bs-target="#resultModal" onclick="RPSSelecter('바위')">
            </button>
            <button type="button" class="btn btn-outline-secondary border-0" id="user" name="user" value="보"
                data-bs-toggle="modal" data-bs-target="#resultModal" onclick="RPSSelecter('보')">
            </button>
        </div>

        <div style="margin:50px; font-size:50px; font-weight: 500;">vs</div>

        <!-- 컴퓨터 랜덤 가위바위보 -->
        <div style="height: 300px;">
            <input type="hidden" id="ComputerVal" name="ComputerVal" value="가위" />
            <img id="ComputerImg" name="ComputerImg" src="http://itsys.hansung.ac.kr/WebEditor/upload/images/gawi.gif">
        </div>

        <!-- 결과 모달창 띄우기(KH) -->
        <div class="modal fade" id="resultModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
            aria-labelledby="resultModalLabel" aria-hidden="true">
            <div class="modal-dialog modal-dialog-centered">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title" id="resultModalLabel">결과</h5>
                    </div>
                    <div class="modal-body" id="resultModalBody">
                    </div>
                    <div class="modal-footer">
                        <p class="text-muted small">계속하시려면 바깥 공간을 클릭하세요.</p>
                    </div>
                </div>
            </div>
        </div>

        <!-- 사용자 랭킹 모달 button -->
        <div class="container text-center">
            <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#topUsersModal"
                style="float: right;">
                사용자 순위 보기
            </button>

        </div>

        <!-- 사용자 랭킹 모달 -->
        <div class="modal fade" id="topUsersModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
            aria-labelledby="topUsersModalLabel" aria-hidden="true">
            <div class="modal-dialog modal-dialog-centered">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title" id="topUsersModalLabel">Top Users</h5>
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                    </div>
                    <div class="modal-body">
                        <table class="table table-hover">
                            <thead>
                                <tr>
                                    <th scope="col" width="10%">순위</th>
                                    <th scope="col" width="20%">사용자</th>
                                    <th scope="col" width="15%"></th>
                                    <th scope="col" width="15%"></th>
                                    <th scope="col" width="15%"></th>
                                </tr>
                            </thead>
                            <tbody>
                                {% for user in ranking %}
                                <tr>
                                    <th scope="row">{{ loop.index }}</th>
                                    <td>{{ user.username }}</td>
                                    <td>{{ user.win }}</td>
                                    <td>{{ user.lose }}</td>
                                    <td>{{ user.draw }}</td>
                                </tr>
                                {% endfor %}
                            </tbody>
                        </table>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                    </div>
                </div>
            </div>
        </div>

        <div style="text-align: left;">
            <button type="button" class="btn btn-primary font-weight-bold">승: {{ reports.win }}</button>
            <button type="button" class="btn btn-danger font-weight-bold">패: {{ reports.lose }}</button>
            <button type="button" class="btn btn-success font-weight-bold">무: {{ reports.draw }}</button>
        </div>

        <table class="table table-hover">
            <thead>
                <tr>
                    <th scope="col" width="10%">#</th>
                    <th scope="col" width="20%">사용자</th>
                    <th scope="col" width="20%">컴퓨터</th>
                    <th scope="col" width="20%">결과</th>
                    <th scope="col" width="30%">날짜</th>
                </tr>
            </thead>
            <tbody>
                {% for data in record %}
                <tr>
                    <th scope="row">{{ loop.index }}</th>
                    <td>{{ data.user }}</td>
                    <td>{{ data.computer }}</td>
                    <td>{{ data.result }}</td>
                    <td>{{ data.GameDay }}</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="{{ url_for('static', filename='confetti_v2.js') }}"></script>
    <script>
        // DB 변경 사항이 페이지에 즉시 반영되지 않는 오류, form 태그를 사용해야하는 요구 사항에 맞지 않음
        // Ajax 비동기식 데이터 전송
        function RPSSelecter(user) {
            var params = {
                user: user,
                ComputerVal: $("#ComputerVal").val(),
            }

            $.ajax({
                url: '/receive/data/',
                type: 'POST',
                data: params,
                success: function (response) {
                    // 모달에 결과 표시(KH)
                    var resultText = `사용자 : ${response.user}\n컴퓨터 : ${response.computer}\n결과 : ${response.result}`;
                    document.getElementById('resultModalBody').innerText = resultText;

                    // 승리 시 이벤트
                    if (response.result == '승') {
                        $("#startButton").trigger("click");
                        $('#canvas').show();
                    }

                    // 3초 뒤에 새로고침
                    setTimeout(function () {
                        window.location.reload();
                    }, 3000);
                },
                error: function (error) {
                    console.log('Error:', error);
                }
            });
        }

        // 가위바위보 이미지
        var RPSList = [
            { val: '가위', img: 'http://itsys.hansung.ac.kr/WebEditor/upload/images/gawi.gif' },
            { val: '바위', img: 'http://itsys.hansung.ac.kr/WebEditor/upload/images/bawi.gif' },
            { val: '보', img: 'http://itsys.hansung.ac.kr/WebEditor/upload/images/bo.gif' },

        ];

        var RPSIndex = 0;

        function switchImage() {
            RPSIndex = (RPSIndex + 1) % RPSList.length;
            document.getElementById('ComputerVal').value = RPSList[RPSIndex].val;
            document.getElementById('ComputerImg').src = RPSList[RPSIndex].img;
        }

        // 0.1초마다 이미지 변경
        setInterval(switchImage, 100);

    </script>
</body>

</html>

app.py

# import random
import json
from flask import Flask, render_template, request, redirect, url_for, session, jsonify
from datetime import datetime
import os
from flask_sqlalchemy import SQLAlchemy


# DB 코드
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.secret_key = 'qwerklsmjacveio'
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///" + os.path.join(
    basedir, "database.db"
)

db = SQLAlchemy(app)


class RPSGame(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user = db.Column(db.String(100), primary_key=False)
    computer = db.Column(db.String(100), primary_key=False)
    result = db.Column(db.String(100), primary_key=False)
    GameDay = db.Column(db.String(100), primary_key=False)
    username = db.Column(db.String(100), primary_key=False)  # 필터로 사용자별로 구분용


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(100), primary_key=False)
    password = db.Column(db.String(100), primary_key=False)


class ranking(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(100), primary_key=False)
    win = db.Column(db.Integer, primary_key=False)
    lose = db.Column(db.Integer, primary_key=False)
    draw = db.Column(db.Integer, primary_key=False)


with app.app_context():
    db.create_all()

rsplist = ['가위', '바위', '보']  # 가위바위보 양식 맞는지 비교용


@app.route("/")
def view():
    return 'mainpage<a href="/signin">로그인</a><br><a href="/signup">회원가입</a>'  # html제작 필요


@app.route("/test")
def test():
    if "userID" in session:
        return render_template("signout.html", data=session["userID"], login=True)
    else:
        return render_template("signout.html", login=False)


# 로그인 폼
@app.route("/signin")
def signin():
    return render_template("signin.html")

# 회원가입 폼


@app.route('/signup')
def signupweb():
    return render_template("signup.html")


# 로그인 하면 처리하러 오는곳
@app.route("/signin_data", methods=["POST"])
def signin_data():
    username = request.form["username"]
    password = request.form["password"]

    # 아이디 비번 짝 맞으면 로그인 성공
    id = User.query.filter_by(username=username).first()
    if id:
        print("id exist")
        if id.password == password:
            print("로그인 성공")
            session["userID"] = username
            return redirect(url_for('home'))  # 로그인 성공하면 메인 주소로 보내기(home바꾸기)
        else:
            print("incorrect")
            return redirect(url_for("signin"))
    else:
        print("id not exist")
        return redirect(url_for("signin"))


# 회원가입 하면 처리하러 오는곳
@app.route("/signup_data", methods=["POST"])
def signup_data():
    username = request.form["username"]
    password = request.form["password"]

    # 이미 아이디가 있는 경우
    if User.query.filter_by(username=username).first():
        print("already exist")
        return redirect(url_for("signin"))  # 아이디가 있으니 로그인 화면으로

    # 아이디가 없으면 생성
    new_user = User(username=username, password=password)
    userReports = ranking(username=username, win=0, lose=0, draw=0)
    db.session.add(userReports)
    db.session.add(new_user)
    db.session.commit()
    print("회원가입 완료")
    return redirect(url_for("signin"))  # 회원가입 후 로그인하러home으로

# 로그아웃

@app.route('/signout')
def signout():
    session.pop("userID")
    return redirect(url_for("view"))


@app.route("/game")  # 가위바위보 고르는 페이지, 모달에서 처리 했던것 처럼 값을 보냄
def home():
    print(session["userID"])
    if "userID" not in session:
        return redirect(url_for("view"))
    record = RPSGame.query.filter_by(username=session["userID"]).all()
    # record.reverse()  # DB 최근 등록 순으로 불러오기
    # 1번부터 출력할지 마지막부터 출력할지 회의
    reports = ranking.query.filter_by(username=session["userID"]).first()
    rankList = top_users()
    # 전역 변수 reports 읽기 및 참조
    return render_template("index.html", record=record, reports=reports, ranking=rankList)


# @app.route('/receive/data/', methods=['POST'])
@app.route(
    "/top_users"
)  # 상위 10명의 사용자를 표시하는 모달. (버튼 눌러서 모달을 띄우고 다시 닫을 수 있는 방식으로 구현)
def top_users():
    # 많이 승리한 사용자 순으로 정렬하며 동점자의 경우 적게 패배한 사용자가 높이 랭킹.
    top_users = (
        ranking.query.order_by(
            ranking.win.desc(), ranking.lose.asc()).limit(10).all()
    )
    return top_users


@app.route("/receive/data/", methods=["POST"])
def get_data():

    today = datetime.now()
    user = request.form.get("user")
    computer = request.form.get("ComputerVal")
    result = ""

    userReports = ranking.query.filter_by(username=session["userID"]).first()
    if not userReports:  # 첫판이면
        userReports = ranking(username=session["userID"],
                              win=0, lose=0, draw=0)

    if computer == user:
        result = "무"
        userReports.draw += 1
    elif rsplist[rsplist.index(user) - 1] == computer:
        result = "승"
        userReports.win += 1
    elif rsplist[rsplist.index(user) - 2] == computer:
        result = "패"
        userReports.lose += 1
    firstGame = RPSGame.query.filter_by(username=session["userID"]).all()
    if firstGame:
        firstGame.reverse()
        print(firstGame[0].id)
    game = RPSGame(
        user=user,
        computer=computer,
        result=result,
        GameDay=today.strftime("%Y-%m-%d %H:%M:%S"),
        username=session["userID"]
    )

    db.session.add(userReports)
    db.session.add(game)
    db.session.commit()

    # return redirect(url_for("home"))
    return jsonify(user=user, computer=computer, result=result)


if __name__ == "__main__":
    app.run(debug=True)

오늘의 회고

잘 참여 못함.

팀원분들이 한 부분 이해하고 따라만 갔다.

 

내일의 목표는 과제발표 마무리 + 다른 팀 구경하면서 배우기