Skip to content

Commit

Permalink
Merge pull request #71 from omochi/carton-example
Browse files Browse the repository at this point in the history
Cartonによる実装サンプルを含める
  • Loading branch information
omochi authored Apr 22, 2024
2 parents cc641de + 83dd496 commit 811b0c8
Show file tree
Hide file tree
Showing 5 changed files with 370 additions and 0 deletions.
8 changes: 8 additions & 0 deletions CartonExample/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
104 changes: 104 additions & 0 deletions CartonExample/Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"pins" : [
{
"identity" : "carton",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftwasm/carton",
"state" : {
"revision" : "85fa55e4f9cbfa68d37dc7050ba7952fed300a2c",
"version" : "1.0.3"
}
},
{
"identity" : "javascriptkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftwasm/JavaScriptKit",
"state" : {
"revision" : "3b5af3d442179900455307c725fe6a111a714b27",
"version" : "0.19.2"
}
},
{
"identity" : "swift-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-algorithms",
"state" : {
"revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42",
"version" : "1.2.0"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"state" : {
"revision" : "46989693916f56d1186bd59ac15124caef896560",
"version" : "1.3.1"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
"version" : "1.2.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb",
"version" : "1.1.0"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5",
"version" : "1.5.4"
}
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "fc63f0cf4e55a4597407a9fc95b16a2bc44b4982",
"version" : "2.64.0"
}
},
{
"identity" : "swift-numerics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-numerics.git",
"state" : {
"revision" : "0a5bc04095a675662cf24757cc0640aa2204253b",
"version" : "1.0.2"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "025bcb1165deab2e20d4eaba79967ce73013f496",
"version" : "1.2.1"
}
},
{
"identity" : "wasmtransformer",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftwasm/WasmTransformer",
"state" : {
"revision" : "d04b31f61b6f528a9a96ebfe4fa4275e333eba82",
"version" : "0.5.0"
}
}
],
"version" : 2
}
23 changes: 23 additions & 0 deletions CartonExample/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// swift-tools-version: 5.9

import PackageDescription

let package = Package(
name: "TicTacToe",
platforms: [.macOS(.v14)],
dependencies: [
.package(url: "https://github.com/swiftwasm/carton", from: "1.0.3"),
.package(path: "../")
],
targets: [
.executableTarget(
name: "TicTacToe",
dependencies: [
.product(name: "React", package: "swift-react")
],
resources: [
.copy("styles.css")
]
)
]
)
155 changes: 155 additions & 0 deletions CartonExample/Sources/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import React
import JavaScriptKit
import SRTDOM

// https://ja.react.dev/learn/tutorial-tic-tac-toe

func addCSS(path: String) throws {
let document = JSWindow.global.document

let head = try document.querySelector("head").unwrap("head")

let tag = try document.createElement("link")
try tag.setAttribute("rel", "stylesheet")
try tag.setAttribute("type", "text/css")
try tag.setAttribute("href", "/.build/wasm32-unknown-wasi/debug/" + path)
try head.appendChild(tag)
}

func calculateWinner(squares: [String?]) -> String? {
let lines: [[Int]] = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
]
for line in lines {
let (a, b, c) = (line[0], line[1], line[2])

if let x = squares[a],
squares[b] == x,
squares[c] == x {
return x
}
}
return nil
}

struct Square: Component {
var value: String?
var onSquareClick: EventListener

func render() -> Node {
return button(
attributes: ["class": "square"],
listeners: ["click": onSquareClick]
) {
value
}
}
}

struct Board: Component {
var xIsNext: Bool = true
var squares: [String?] = Array(repeating: nil, count: 9)
var onPlay: Function<Void, [String?]>

func handleClick(index: Int) {
if squares[index] != nil ||
calculateWinner(squares: squares) != nil { return }

var squares = self.squares

if xIsNext {
squares[index] = "X"
} else {
squares[index] = "O"
}

onPlay(squares)
}

func render() -> Node {
let winner = calculateWinner(squares: squares)
let status: String
if let winner {
status = "Winner: " + winner;
} else {
status = "Next player: " + (xIsNext ? "X" : "O")
}

return Fragment {
div(attributes: ["class": "status"]) { status }
div(attributes: ["class": "board-row"]) {
Square(value: squares[0], onSquareClick: EventListener { (_) in handleClick(index: 0) })
Square(value: squares[1], onSquareClick: EventListener { (_) in handleClick(index: 1) })
Square(value: squares[2], onSquareClick: EventListener { (_) in handleClick(index: 2) })
}
div(attributes: ["class": "board-row"]) {
Square(value: squares[3], onSquareClick: EventListener { (_) in handleClick(index: 3) })
Square(value: squares[4], onSquareClick: EventListener { (_) in handleClick(index: 4) })
Square(value: squares[5], onSquareClick: EventListener { (_) in handleClick(index: 5) })
}
div(attributes: ["class": "board-row"]) {
Square(value: squares[6], onSquareClick: EventListener { (_) in handleClick(index: 6) })
Square(value: squares[7], onSquareClick: EventListener { (_) in handleClick(index: 7) })
Square(value: squares[8], onSquareClick: EventListener { (_) in handleClick(index: 8) })
}
}
}
}

struct Game: Component {
@State var history: [[String?]] = [Array(repeating: nil, count: 9)]
@State var currentMove: Int = 0

func render() -> Node {
let xIsNext = currentMove % 2 == 0

let currentSquares = history[currentMove]

let handlePlay = Function<Void, [String?]> { (nextSquares) in
history = history[...currentMove] + [nextSquares]
currentMove = history.count - 1
}

func jumpTo(nextMove: Int) {
currentMove = nextMove
}

let moves: [Node] = history.enumerated().map { (move, squares) -> Node in
let description: String
if move > 0 {
description = "Go to move #\(move)"
} else {
description = "Go to game start"
}
return li(key: move) {
button(listeners: ["click": EventListener { (e) in jumpTo(nextMove: move) }]) {
description
}
}
}

return div(attributes: ["class": "game"]) {
div(attributes: ["class": "game-board"]) {
Board(xIsNext: xIsNext, squares: currentSquares, onPlay: handlePlay)
}
div(attributes: ["class": "game-info"]) {
ol {
moves
}
}
}
}
}

try addCSS(path: "TicTacToe_TicTacToe.resources/styles.css")

let body = try JSWindow.global.document.body.unwrap("body")
let root = ReactRoot(element: body)
root.render(node: Game())
80 changes: 80 additions & 0 deletions CartonExample/Sources/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
* {
box-sizing: border-box;
}

body {
font-family: sans-serif;
margin: 20px;
padding: 0;
}

h1 {
margin-top: 0;
font-size: 22px;
}

h2 {
margin-top: 0;
font-size: 20px;
}

h3 {
margin-top: 0;
font-size: 18px;
}

h4 {
margin-top: 0;
font-size: 16px;
}

h5 {
margin-top: 0;
font-size: 14px;
}

h6 {
margin-top: 0;
font-size: 12px;
}

code {
font-size: 1.2em;
}

ul {
padding-inline-start: 20px;
}

.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 34px;
}

.board-row:after {
clear: both;
content: '';
display: table;
}

.status {
margin-bottom: 10px;
}
.game {
display: flex;
flex-direction: row;
}

.game-info {
margin-left: 20px;
}

0 comments on commit 811b0c8

Please sign in to comment.