<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ELDER - ソーシャルネットワーキングサービス</title>
<!-- CSSファイルの読み込み -->
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet">
<!-- RiTa.jsライブラリの読み込み -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/rita/1.3.63/rita-full.min.js"></script>
<style>
/* 既存のスタイルシート */
body {
padding-top: 20px;
background: linear-gradient(to right, #f8f9fa, #e9ecef);
font-family: 'Arial', sans-serif;
color: #343a40;
}
.navbar {
background-color: #007bff;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.navbar-brand,
.nav-link {
color: #fff !important;
font-weight: bold;
}
.navbar-toggler {
border: none;
}
.card {
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}
.card-body {
padding: 30px;
}
.form-control {
border-radius: 20px;
border: 1px solid #ced4da;
padding: 15px;
transition: border-color 0.3s;
}
.form-control:focus {
border-color: #007bff;
box-shadow: none;
}
.btn-primary {
background-color: #007bff;
border: none;
border-radius: 20px;
padding: 10px 20px;
font-weight: bold;
transition: background-color 0.3s, transform 0.3s;
}
.btn-primary:hover {
background-color: #0056b3;
transform: translateY(-3px);
}
.btn-secondary {
border-radius: 20px;
padding: 10px 20px;
font-weight: bold;
transition: background-color 0.3s, transform 0.3s;
}
.btn-secondary:hover {
background-color: #6c757d;
transform: translateY(-3px);
}
.profile-img {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.text-center h1 {
font-weight: 700;
font-size: 2.5em;
margin-bottom: 20px;
color: #007bff;
}
.comment-section {
margin-top: 20px;
padding: 20px;
background-color: #f1f3f5;
border-radius: 8px;
}
.comment-section .card {
border: none;
background: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.modal-content {
border-radius: 15px;
overflow: hidden;
}
.modal-header {
background-color: #007bff;
color: white;
border-bottom: none;
}
.modal-footer {
border-top: none;
}
.form-error {
color: red;
font-size: 0.9em;
}
/* タイムラインのデザイン */
#timeline {
margin-top: 20px;
}
#timeline .card {
border-radius: 15px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
overflow: hidden;
background-color: #fff;
}
#timeline .card-body {
padding: 20px;
}
#timeline .btn {
border-radius: 20px;
}
/* 投稿のシェアボタン */
.btn-share {
background-color: #ff9800;
border: none;
border-radius: 20px;
padding: 5px 10px;
color: #fff;
font-weight: bold;
transition: background-color 0.3s;
}
.btn-share:hover {
background-color: #e68900;
}
/* X(旧Twitter)シェアボタン */
.btn-share-twitter {
background-color: #1da1f2;
border: none;
border-radius: 20px;
padding: 5px 10px;
color: #fff;
font-weight: bold;
transition: background-color 0.3s;
}
.btn-share-twitter:hover {
background-color: #1480bf;
}
/* 通知エリア */
#notificationArea {
position: fixed;
top: 80px;
right: 20px;
z-index: 1000;
max-width: 300px;
}
/* FEEDアイコンをRSSの文字に変更 */
.nav-item .rss-icon {
font-weight: bold;
color: #fff;
}
</style>
</head>
<body>
<!-- ナビゲーションバー -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">ELDER</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="ナビゲーションの切り替え">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="#" onclick="showLoginForm()">ログイン</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="showRegisterForm()">登録</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="showProfile()" style="display: none;" id="navProfile">プロフィール</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="logout()" style="display: none;" id="navLogout">ログアウト</a>
</li>
<!-- FEEDアイコンをRSSの文字に変更 -->
<li class="nav-item" id="navFeed" style="display: none;">
<a class="nav-link rss-icon" href="#" onclick="showFeedForm()">RSS</a>
</li>
</ul>
</div>
</nav>
<div class="container">
<!-- ログインフォーム -->
<div id="loginForm" class="card w-50 mx-auto">
<div class="card-body">
<h2 class="card-title text-center mb-4">ログイン</h2>
<div class="form-group">
<input type="text" id="loginUsername" class="form-control" placeholder="ユーザー名">
<span id="loginUsernameError" class="form-error" style="display: none;">ユーザー名を入力してください</span>
</div>
<div class="form-group">
<input type="password" id="loginPassword" class="form-control" placeholder="パスワード">
<span id="loginPasswordError" class="form-error" style="display: none;">パスワードを入力してください</span>
</div>
<button onclick="login()" class="btn btn-primary btn-block">ログイン</button>
<button onclick="showRegisterForm()" class="btn btn-secondary btn-block">登録</button>
</div>
</div>
<!-- 登録フォーム -->
<div id="registerForm" class="card w-50 mx-auto" style="display: none;">
<div class="card-body">
<h2 class="card-title text-center mb-4">登録</h2>
<div class="form-group">
<input type="text" id="registerName" class="form-control" placeholder="氏名">
<span id="registerNameError" class="form-error" style="display: none;">氏名を入力してください</span>
</div>
<div class="form-group">
<input type="text" id="registerUsername" class="form-control" placeholder="ユーザー名">
<span id="registerUsernameError" class="form-error" style="display: none;">ユーザー名を入力してください</span>
</div>
<div class="form-group">
<input type="password" id="registerPassword" class="form-control" placeholder="パスワード">
<span id="registerPasswordError" class="form-error" style="display: none;">パスワードを入力してください</span>
</div>
<div class="form-group">
<input type="password" id="registerConfirmPassword" class="form-control" placeholder="パスワードを確認">
<span id="registerConfirmPasswordError" class="form-error" style="display: none;">パスワードが一致しません</span>
</div>
<button onclick="register()" class="btn btn-primary btn-block">登録</button>
<button onclick="showLoginForm()" class="btn btn-secondary btn-block">ログインに戻る</button>
</div>
</div>
<!-- プロフィール -->
<div id="profile" class="card w-50 mx-auto" style="display: none;">
<div class="card-body">
<h2 class="card-title text-center mb-4">プロフィール</h2>
<div class="text-center mb-3">
<img id="profileImg" class="profile-img" src="default_profile_img.jpg" alt="プロフィール画像">
</div>
<p class="text-center mb-2"><strong>氏名:</strong> <span id="profileName"></span></p>
<p class="text-center mb-4"><strong>ユーザー名:</strong> <span id="profileUsername"></span></p>
<div class="form-group">
<input type="file" id="profileImageInput" accept="image/*" class="form-control-file">
</div>
<button onclick="uploadProfileImage()" class="btn btn-primary btn-block"><i class="fas fa-upload"></i> プロフィール画像をアップロード</button>
<button onclick="logout()" class="btn btn-danger btn-block"><i class="fas fa-sign-out-alt"></i> ログアウト</button>
</div>
</div>
<!-- 投稿フォーム -->
<div id="postForm" class="card w-75 mx-auto" style="display: none;">
<div class="card-body">
<h2 class="card-title text-center mb-4">投稿を作成</h2>
<div class="form-group">
<textarea id="postContent" class="form-control" rows="3" placeholder="今何を考えていますか?"></textarea>
</div>
<button onclick="createPost()" class="btn btn-primary btn-block">投稿</button>
</div>
</div>
<!-- FEED登録フォーム -->
<div id="feedForm" class="card w-75 mx-auto" style="display: none;">
<div class="card-body">
<h2 class="card-title text-center mb-4">FEEDを登録</h2>
<div class="form-group">
<input type="text" id="feedUrl" class="form-control" placeholder="RSSフィードのURL">
<span id="feedUrlError" class="form-error" style="display: none;">RSSフィードのURLを入力してください</span>
</div>
<button onclick="registerFeed()" class="btn btn-primary btn-block">登録</button>
<div class="form-group mt-4">
<label for="feedInterval">自動投稿の間隔(秒):</label>
<input type="number" id="feedInterval" class="form-control" placeholder="例:60" min="1">
</div>
<button onclick="setFeedInterval()" class="btn btn-secondary btn-block">間隔を設定</button>
</div>
</div>
<!-- AIBOT投稿登録フォーム -->
<div id="aiBotForm" class="card w-75 mx-auto" style="display: none;">
<div class="card-body">
<h2 class="card-title text-center mb-4">BOT投稿を登録</h2>
<div class="form-group">
<textarea id="aiBotContent" class="form-control" rows="3" placeholder="BOTに投稿させたい内容を入力してください"></textarea>
</div>
<button onclick="registerAiBotPost()" class="btn btn-primary btn-block">BOT投稿を登録</button>
</div>
</div>
<!-- タイムライン -->
<div id="timeline" class="w-75 mx-auto mt-4"></div>
<!-- 通知エリア -->
<div id="notificationArea" class="alert alert-info" style="display: none;"></div>
</div>
<!-- リプライモーダル -->
<div class="modal fade" id="replyModal" tabindex="-1" role="dialog" aria-labelledby="replyModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<!-- モーダルの内容は後ほどJavaScriptで制御します -->
<div class="modal-header">
<h5 class="modal-title" id="replyModalLabel">投稿に返信</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="閉じる">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<textarea id="replyContent" class="form-control" rows="3" placeholder="返信をここに書いてください..."></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">閉じる</button>
<button type="button" onclick="postReply()" class="btn btn-primary">返信</button>
</div>
</div>
</div>
</div>
<!-- スクリプトの読み込み -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<!-- BootstrapのJavaScript -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<!-- RiTa.jsライブラリの読み込み(既にhead内で読み込み済み) -->
<script>
// ユーザー、投稿、フィード、チャット関連のデータ
let currentUser = null;
let users = [];
let posts = [];
let feeds = [];
let notifications = [];
let aiBotPosts = [];
let feedFetchInterval = 60000; // デフォルトは60,000ミリ秒(60秒)
let feedFetchTimer;
let markovBotInterval = 60000; // マルコフ連鎖BOTの投稿間隔
// ローカルストレージの読み込み・保存関連の関数
function saveUsersToLocalStorage() {
localStorage.setItem('users', JSON.stringify(users));
}
function savePostsToLocalStorage() {
localStorage.setItem('posts', JSON.stringify(posts));
}
function saveFeedsToLocalStorage() {
localStorage.setItem('feeds', JSON.stringify(feeds));
}
function saveAiBotPostsToLocalStorage() {
localStorage.setItem('aiBotPosts', JSON.stringify(aiBotPosts));
}
function saveCurrentUserToLocalStorage() {
if (currentUser) {
localStorage.setItem('currentUser', JSON.stringify(currentUser));
} else {
localStorage.removeItem('currentUser');
}
}
function loadUsersFromLocalStorage() {
const storedUsers = localStorage.getItem('users');
if (storedUsers) {
users = JSON.parse(storedUsers);
}
}
function loadPostsFromLocalStorage() {
const storedPosts = localStorage.getItem('posts');
if (storedPosts) {
posts = JSON.parse(storedPosts);
}
}
function loadFeedsFromLocalStorage() {
const storedFeeds = localStorage.getItem('feeds');
if (storedFeeds) {
feeds = JSON.parse(storedFeeds);
}
}
function loadAiBotPostsFromLocalStorage() {
const storedAiBotPosts = localStorage.getItem('aiBotPosts');
if (storedAiBotPosts) {
aiBotPosts = JSON.parse(storedAiBotPosts);
}
}
function loadCurrentUserFromLocalStorage() {
const storedCurrentUser = localStorage.getItem('currentUser');
if (storedCurrentUser) {
currentUser = JSON.parse(storedCurrentUser);
}
}
// フォームの表示・非表示
function hideAll() {
document.getElementById('loginForm').style.display = 'none';
document.getElementById('registerForm').style.display = 'none';
document.getElementById('profile').style.display = 'none';
document.getElementById('postForm').style.display = 'none';
document.getElementById('feedForm').style.display = 'none';
document.getElementById('aiBotForm').style.display = 'none';
}
function showLoginForm() {
hideAll();
document.getElementById('loginForm').style.display = 'block';
document.getElementById('navProfile').style.display = 'none';
document.getElementById('navLogout').style.display = 'none';
document.getElementById('navFeed').style.display = 'none';
}
function showRegisterForm() {
hideAll();
document.getElementById('registerForm').style.display = 'block';
}
function showProfile() {
hideAll();
document.getElementById('profile').style.display = 'block';
document.getElementById('profileName').textContent = currentUser.name;
document.getElementById('profileUsername').textContent = currentUser.username;
document.getElementById('postForm').style.display = 'block';
document.getElementById('feedForm').style.display = 'block';
document.getElementById('aiBotForm').style.display = 'block';
loadProfileImage(); // プロフィール画像をロード
displayTimeline(); // タイムラインを表示
document.getElementById('navProfile').style.display = 'block';
document.getElementById('navLogout').style.display = 'block';
document.getElementById('navFeed').style.display = 'block'; // RSSナビゲーションを表示
}
function showFeedForm() {
hideAll();
document.getElementById('feedForm').style.display = 'block';
}
// ユーザー認証関連の関数
function validateLoginForm() {
let valid = true;
const username = document.getElementById('loginUsername').value;
const password = document.getElementById('loginPassword').value;
if (!username) {
document.getElementById('loginUsernameError').style.display = 'block';
valid = false;
} else {
document.getElementById('loginUsernameError').style.display = 'none';
}
if (!password) {
document.getElementById('loginPasswordError').style.display = 'block';
valid = false;
} else {
document.getElementById('loginPasswordError').style.display = 'none';
}
return valid;
}
function login() {
if (!validateLoginForm()) {
return;
}
const username = document.getElementById('loginUsername').value;
const password = document.getElementById('loginPassword').value;
const user = users.find(u => u.username === username && u.password === password);
if (user) {
currentUser = user;
saveCurrentUserToLocalStorage(); // currentUserを保存
showProfile();
} else {
alert('ユーザー名またはパスワードが間違っています。');
}
}
function logout() {
currentUser = null;
saveCurrentUserToLocalStorage(); // currentUserを削除
hideAll();
showLoginForm();
}
function validateRegisterForm() {
let valid = true;
const name = document.getElementById('registerName').value;
const username = document.getElementById('registerUsername').value;
const password = document.getElementById('registerPassword').value;
const confirmPassword = document.getElementById('registerConfirmPassword').value;
if (!name) {
document.getElementById('registerNameError').style.display = 'block';
valid = false;
} else {
document.getElementById('registerNameError').style.display = 'none';
}
if (!username) {
document.getElementById('registerUsernameError').style.display = 'block';
valid = false;
} else {
document.getElementById('registerUsernameError').style.display = 'none';
}
// ユーザー名の重複チェック
if (users.find(u => u.username === username)) {
document.getElementById('registerUsernameError').textContent = 'このユーザー名は既に使用されています';
document.getElementById('registerUsernameError').style.display = 'block';
valid = false;
} else {
document.getElementById('registerUsernameError').style.display = 'none';
}
if (!password) {
document.getElementById('registerPasswordError').style.display = 'block';
valid = false;
} else {
document.getElementById('registerPasswordError').style.display = 'none';
}
if (password !== confirmPassword) {
document.getElementById('registerConfirmPasswordError').style.display = 'block';
valid = false;
} else {
document.getElementById('registerConfirmPasswordError').style.display = 'none';
}
return valid;
}
function register() {
if (!validateRegisterForm()) {
return;
}
const name = document.getElementById('registerName').value;
const username = document.getElementById('registerUsername').value;
const password = document.getElementById('registerPassword').value;
users.push({ name, username, password, profileImage: null });
saveUsersToLocalStorage(); // ユーザー情報を保存
alert('登録が完了しました!ログインしてください。');
showLoginForm();
}
// プロフィール画像の処理
function loadProfileImage() {
const profileImg = document.getElementById('profileImg');
// プロフィール画像が設定されている場合はその画像を表示
if (currentUser.profileImage) {
profileImg.src = currentUser.profileImage;
} else {
// デフォルトのプロフィール画像を表示
profileImg.src = 'default_profile_img.jpg';
}
}
function uploadProfileImage() {
const input = document.getElementById('profileImageInput');
const file = input.files[0];
if (file) {
// FileReaderを使用して画像を読み込む
const reader = new FileReader();
reader.onload = function(e) {
// 読み込んだ画像をプロフィール画像として設定
currentUser.profileImage = e.target.result;
// プロフィール画像を更新して表示
loadProfileImage();
saveUsersToLocalStorage(); // プロフィール画像を保存
saveCurrentUserToLocalStorage(); // currentUserを更新
}
reader.readAsDataURL(file);
}
}
// AIBOT投稿を登録
function registerAiBotPost() {
const content = document.getElementById('aiBotContent').value;
if (content.trim() !== '') {
aiBotPosts.push(content);
saveAiBotPostsToLocalStorage();
alert("BOTの投稿が登録されました!");
document.getElementById('aiBotContent').value = ''; // 入力欄を空にする
}
}
// AIBOTが定期的に投稿
function postAiBotContent() {
if (aiBotPosts.length > 0) {
const content = aiBotPosts[Math.floor(Math.random() * aiBotPosts.length)];
const aiUser = {
name: 'BOT',
username: 'bot',
profileImage: 'default_ai_profile_img.jpg'
};
const post = {
id: Date.now(),
content: content,
author: aiUser.name,
authorUsername: aiUser.username,
authorProfileImage: aiUser.profileImage,
likes: 0,
comments: []
};
posts.unshift(post);
savePostsToLocalStorage();
displayTimeline();
}
}
setInterval(postAiBotContent, 500000); // 50,000ミリ秒 = 50秒
// マルコフ連鎖を使用した自動投稿BOT
function generateMarkovChainText(order = 2) {
const allTexts = posts.map(post => post.content).join(' ');
if (allTexts.length < 100) return null; // テキストが短すぎる場合は生成しない
const markov = new RiTa.Markov(order);
markov.addText(allTexts);
const sentences = markov.generate(1);
return sentences[0];
}
function postMarkovBotContent() {
const generatedText = generateMarkovChainText(3); // 階数を3に設定
if (generatedText) {
const botUser = {
name: 'MarkovBot',
username: 'markovbot',
profileImage: 'markov_bot_profile_img.jpg'
};
const post = {
id: Date.now(),
content: generatedText,
author: botUser.name,
authorUsername: botUser.username,
authorProfileImage: botUser.profileImage,
likes: 0,
comments: []
};
posts.unshift(post);
savePostsToLocalStorage();
displayTimeline();
}
}
setInterval(postMarkovBotContent, markovBotInterval); // マルコフ連鎖BOTの投稿間隔
// 投稿関連の関数
function createPost() {
const postContent = document.getElementById('postContent').value;
if (postContent.trim() !== '') {
const post = {
id: Date.now(),
content: postContent,
author: currentUser.name,
authorUsername: currentUser.username,
authorProfileImage: currentUser.profileImage,
likes: 0,
comments: [],
shares: 0
};
posts.unshift(post); // 最新の投稿を先頭に追加
savePostsToLocalStorage(); // 投稿を保存
displayTimeline();
document.getElementById('postContent').value = ''; // 投稿後、入力欄を空にする
}
}
function displayTimeline() {
const timeline = document.getElementById('timeline');
timeline.innerHTML = '';
posts.forEach(post => {
const postElement = document.createElement('div');
postElement.classList.add('card', 'mb-3');
postElement.innerHTML = `
<div class="card-body">
<div class="d-flex align-items-center mb-3">
<img src="${post.authorProfileImage || 'default_profile_img.jpg'}" class="profile-img mr-2" alt="プロフィール画像">
<div>
<strong>${post.author}</strong> (@${post.authorUsername})
${post.shared ? '<span class="badge badge-info ml-2">シェア</span>' : ''}
</div>
</div>
<p class="card-text">${post.content}</p>
${post.link ? `<a href="${post.link}" target="_blank" class="btn btn-link">続きを読む</a>` : ''}
<div class="d-flex justify-content-between">
<div>
<button onclick="likePost(${post.id})" class="btn btn-primary btn-sm"><i class="fas fa-thumbs-up"></i> いいね (${post.likes})</button>
<button onclick="toggleComments(${post.id})" class="btn btn-secondary btn-sm"><i class="fas fa-comments"></i> コメント (${post.comments.length})</button>
<button onclick="replyToPost(${post.id})" class="btn btn-info btn-sm"><i class="fas fa-reply"></i> 返信</button>
<button onclick="sharePost(${post.id})" class="btn btn-share btn-sm"><i class="fas fa-share"></i> シェア (${post.shares || 0})</button>
<button onclick="shareToTwitter(${post.id})" class="btn btn-share-twitter btn-sm"><i class="fab fa-twitter"></i> Xでシェア</button>
</div>
<small class="text-muted">${new Date(post.id).toLocaleString()}</small>
</div>
<div id="comments-${post.id}" class="comment-section mt-3" style="display: none;">
${post.comments.map(comment => `
<div class="card mb-2">
<div class="card-body">
<p><strong>${comment.author}</strong>: ${comment.content}</p>
</div>
</div>
`).join('')}
</div>
</div>
`;
timeline.appendChild(postElement);
});
}
// いいね機能
function likePost(postId) {
const post = posts.find(p => p.id === postId);
post.likes++;
savePostsToLocalStorage();
displayTimeline();
}
// コメント表示切替
function toggleComments(postId) {
const commentSection = document.getElementById(`comments-${postId}`);
commentSection.style.display = commentSection.style.display === 'none' ? 'block' : 'none';
}
// リプライ機能
function replyToPost(postId) {
const modal = document.getElementById('replyModal');
modal.dataset.postId = postId;
$('#replyModal').modal('show');
}
function postReply() {
const postId = parseInt(document.getElementById('replyModal').dataset.postId);
const post = posts.find(p => p.id === postId);
const replyContent = document.getElementById('replyContent').value;
if (replyContent.trim() !== '') {
post.comments.push({ author: currentUser.name, content: replyContent });
$('#replyModal').modal('hide');
savePostsToLocalStorage();
displayTimeline();
} else {
alert('返信を入力してください。');
}
}
// 投稿のシェア機能
function sharePost(postId) {
const post = posts.find(p => p.id === postId);
if (post) {
const sharedPost = {
...post,
id: Date.now(),
author: currentUser.name,
authorUsername: currentUser.username,
authorProfileImage: currentUser.profileImage,
shared: true,
likes: 0,
comments: [],
shares: post.shares ? post.shares + 1 : 1
};
posts.unshift(sharedPost);
savePostsToLocalStorage();
displayTimeline();
alert('投稿をシェアしました。');
}
}
// X(旧Twitter)にシェアする機能
function shareToTwitter(postId) {
const post = posts.find(p => p.id === postId);
if (post) {
let shareText = post.content;
if (post.link) {
shareText += ' ' + post.link;
}
const shareUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}&url=${encodeURIComponent(window.location.href)}`;
window.open(shareUrl, '_blank');
}
}
// FEEDの登録と自動投稿機能
function registerFeed() {
const feedUrl = document.getElementById('feedUrl').value;
if (feedUrl.trim() !== '') {
feeds.push(feedUrl);
saveFeedsToLocalStorage(); // フィード情報を保存
alert('RSSフィードが登録されました!');
document.getElementById('feedUrl').value = ''; // 登録後、入力欄を空にする
} else {
document.getElementById('feedUrlError').style.display = 'block';
}
}
function fetchFeeds() {
feeds.forEach(feedUrl => {
fetch(`https://api.rss2json.com/v1/api.json?rss_url=${encodeURIComponent(feedUrl)}`)
.then(response => response.json())
.then(data => {
data.items.forEach(item => {
// 同じ投稿が既に存在するかチェック
if (!posts.some(p => p.link === item.link)) {
const post = {
id: Date.now() + Math.random(), // 重複しないIDを生成
content: item.title,
link: item.link, // リンクを追加
author: 'FEEDBOT',
authorUsername: 'feedbot',
authorProfileImage: 'feedbot_profile_img.jpg', // FEEDBOTのプロフィール画像を設定
likes: 0,
comments: []
};
posts.unshift(post); // 最新の投稿を先頭に追加
savePostsToLocalStorage(); // 投稿を保存
}
});
displayTimeline(); // タイムラインを表示
})
.catch(error => console.error('RSSフィードの取得エラー:', error));
});
}
function setFeedInterval() {
const intervalInput = document.getElementById('feedInterval').value;
if (intervalInput && parseInt(intervalInput) > 0) {
feedFetchInterval = parseInt(intervalInput) * 1000; // ミリ秒に変換
clearInterval(feedFetchTimer);
feedFetchTimer = setInterval(fetchFeeds, feedFetchInterval);
alert(`自動投稿の間隔が${intervalInput}秒に設定されました。`);
} else {
alert('有効な間隔を入力してください(1以上の数字)。');
}
}
// ページロード時にフィードの自動取得を開始
function startFeedFetch() {
feedFetchTimer = setInterval(fetchFeeds, feedFetchInterval);
}
// 初期化
function init() {
loadUsersFromLocalStorage();
loadPostsFromLocalStorage();
loadFeedsFromLocalStorage();
loadAiBotPostsFromLocalStorage();
loadCurrentUserFromLocalStorage();
if (currentUser) {
showProfile();
} else {
hideAll();
document.getElementById('loginForm').style.display = 'block';
}
displayTimeline();
startFeedFetch(); // フィードの自動取得を開始
}
// ページロード時に初期化
window.onload = init;
</script>
</body>
</html>
月: 2024年10月
PHP array_map
<?php
// $addFive = function($n)
// {
// return $n + 5;
// };
// $addFive = fn($n) => $n + 5;
$scores = [70, 90, 80];
// $updatedScores = array_map($addFive, $scores);
$updatedScores = array_map(fn($n) => $n + 5, $scores);
print_r($updatedScores);
新作アニメのアイディア
タイトル: 「無限の境界(Infinity Edge)」
ジャンル: SF、ファンタジー、サスペンス
あらすじ:
未来の地球、人類は高度な技術と魔法が融合した世界に生きている。しかし、この世界は「無限の境界」という謎の空間によって二つの異なる次元に分断されていた。一方の次元では人々が技術と魔法を調和させて平和に暮らし、もう一方の次元では技術は廃れ、魔法が暴走し、混乱と戦争が続いている。
主人公は、次元を渡る力を持つ少女「レイナ」。彼女はある日、両次元をつなぐ「無限の境界」を越える使命を与えられ、平和な次元から混乱する次元へと旅立つ。しかし、彼女が境界を越えるたびに、次元のバランスが崩れ、現実そのものが歪んでいくことに気づく。
レイナは、各次元に存在する「守護者」と呼ばれる異能の者たちと協力しながら、両次元を救うための方法を探し出さなければならない。だが、彼女が渡り歩く先々で出会う者たちには、それぞれ異なる真実があり、次元の崩壊には隠された陰謀が絡んでいる。
主なキャラクター:
- レイナ: 主人公。次元を渡る力を持つが、その力は彼女自身も完全には制御できない。優しく聡明だが、次第に重い決断を迫られる。
- エイジ: 混乱する次元の守護者で、冷静かつ戦略的なリーダータイプ。かつては平和な次元の人間だったが、ある事件でこの次元に送り込まれた。
- ミラ: 平和な次元の科学者で、技術と魔法の融合を研究している。レイナを支援し、次元崩壊の原因を探る。
- カイン: 次元崩壊を引き起こしている影の組織のリーダー。両次元を破壊して新しい世界を創ろうとしている野心家。
特徴:
- 次元移動: 異なる次元に行くたびに、風景やルール、物理法則が変化する。魔法が支配する世界、テクノロジーが主流の未来都市、荒廃した戦場など、様々な舞台設定が可能。
- 深い世界観: 技術と魔法が共存する未来、異なる次元の歴史や文化が詳細に描かれ、視聴者を没入させる。
- サスペンス要素: 両次元の背後に隠された秘密や、登場人物たちの裏切り、陰謀が物語を盛り上げる。
- ダークファンタジー要素: レイナは次元を救うために善悪の境界に悩むことになり、苦しい選択を迫られる。
このアイディアは、壮大なスケールで次元や時間、陰謀が絡み合うSFファンタジーとして展開することができ、視覚的にもドラマティックな演出が期待できると思います
chat.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ローカルチャットシステム</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.chat-container {
width: 80%;
max-width: 600px;
margin: 50px auto;
background-color: white;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 8px;
overflow: hidden;
display: flex;
flex-direction: column;
height: 600px;
}
.chat-header {
background-color: #007bff;
color: white;
padding: 10px;
text-align: center;
font-size: 18px;
}
.chat-messages {
flex-grow: 1;
overflow-y: scroll;
padding: 10px;
background-color: #fafafa;
}
.chat-message {
margin-bottom: 10px;
display: flex;
align-items: center;
}
.chat-message .username {
font-weight: bold;
margin-right: 5px;
}
.message-content {
padding: 8px;
background-color: #e9ecef;
border-radius: 5px;
flex-grow: 1;
}
.message-time {
font-size: 12px;
text-align: right;
margin-left: 10px;
color: #888;
}
.chat-input {
display: flex;
border-top: 1px solid #ddd;
}
.chat-input input {
width: 100%;
padding: 10px;
border: none;
outline: none;
font-size: 16px;
}
.chat-input button {
padding: 10px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
</style>
</head>
<body>
<div class="chat-container">
<div class="chat-header">
ローカルチャットシステム
</div>
<div class="chat-messages" id="chat-messages">
<!-- メッセージがここに表示される -->
</div>
<div class="chat-input">
<input type="text" id="chat-input" placeholder="メッセージを入力...">
<button id="send-button">送信</button>
</div>
</div>
<script>
const messagesContainer = document.getElementById('chat-messages');
const inputField = document.getElementById('chat-input');
const sendButton = document.getElementById('send-button');
// ユーザー情報
const users = ['ユーザー1', 'ユーザー2'];
let currentUserIndex = 0;
// メッセージを追加する関数
function addMessage(content, username, time = new Date().toLocaleTimeString()) {
const messageDiv = document.createElement('div');
messageDiv.classList.add('chat-message');
const userSpan = document.createElement('span');
userSpan.classList.add('username');
userSpan.textContent = username;
messageDiv.appendChild(userSpan);
const messageContent = document.createElement('div');
messageContent.classList.add('message-content');
messageContent.textContent = content;
messageDiv.appendChild(messageContent);
const timeSpan = document.createElement('span');
timeSpan.classList.add('message-time');
timeSpan.textContent = time;
messageDiv.appendChild(timeSpan);
messagesContainer.appendChild(messageDiv);
// スクロールを最新メッセージに合わせる
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
// 送信ボタンのクリックイベント
sendButton.addEventListener('click', function() {
const message = inputField.value;
if (message.trim() !== '') {
const username = users[currentUserIndex];
currentUserIndex = (currentUserIndex + 1) % users.length; // ユーザーを切り替え
addMessage(message, username); // メッセージを表示
inputField.value = ''; // 入力フィールドをクリア
}
});
// エンターキーで送信できるように
inputField.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendButton.click();
}
});
</script>
</body>
</html>
ゲーム開発のアイディア
チャット履歴
Kawaii Illust Maker : イラストかわいいや
今日
昨日
- Enemy Selection Code Fix
- 左クリックで敵選択
- コード修正の提案
- 敵選択機能修正方法
- ソフトウェアアイディア一覧
- テキストエディタ機能拡張
- 宇宙開発アイデア集
- Unreal Engine クラッシュ修正方法
- Unreal Engine 起動エラー 修正
過去 7 日間
- 国旗デザインの提案
- Webブラウザー作成方法
- IsAiming変数の修正方法
- Anime画像生成AI作成
- AI 画像生成 Python コード
- WEBサービスアイディア提案
- 発明アイディアの提案
- イラスト投稿サイト HTMLテンプレート
- 高価なドメインの情報
- 原神のVR化方法
- React 動画共有サービスの作成
- MariaDB Overview and Features
- 新しいチャット
- 新しいチャット
- .hackVR 企画書 提案
- ドメイン取得サイト作成方法
- タイムカードシステム作成方法
- VRMモデル表示の実装
- テレポーテーションの実装方法
- AnimGraphにおけるPlay設定
- ELDER SNS AI Enhancements
- ウェブデザイン改良提案
- 新しいチャット
2023年
- HTML Social Bookmarking Guide
- HTMLでのソーシャルブックマーク
- Anime-style Water Fairy Smile
- Pinterestのようなサービス作成
- 心の疲労と感情の消失
- 改善されたユーザー登録
- Anime Girl Image Request
- Tweet Content Suggestions
- New chat
- PHP ログイン改善
- 修正コード: PHPフォーム
- Create Multiplayer Number Guessing Game
- New chat
- Plan Personal, Family, Professional Resolutions
- 自動メールレポートの作成
- New chat
- クローラー – より多くの情報を提供
- 作成するタイトル: クローラーの作り方
- Notion Database Assistance
- Pythonの基本要素
- New chat
- Create image: Provide details
Team ワークスペースを追加するTeam プランで共同作業する
ChatGPT 4o
共有する
あなた:
ゲーム開発ノ、アイディア
ChatGPT:
ChatGPT
ゲーム開発のアイデアをいくつか提案します。これらは、さまざまなジャンルやテーマに基づいており、あなたのプロジェクトに合ったものを選んでください。
1. マルチプレイヤーRPG:運命の剣
あなたが取り組んでいる『運命の剣』に関連するアイディアとして、オンライン要素を強化したマルチプレイヤーRPGを提案します。プレイヤーは「運命の剣」を手に入れるためにチームを組み、巨大な敵と戦ったり、パズルを解いたりする。戦闘だけでなく、協力プレイが重要になるゲームです。プレイヤーは自分のキャラクターをカスタマイズし、他のプレイヤーとリアルタイムでクエストを進められる要素を追加します。
2. AI駆動のストーリー生成ゲーム
AIを活用したストーリー生成ゲームを提案します。プレイヤーの選択や行動に応じて、AIがリアルタイムで新しい物語やイベントを生成します。これにより、毎回違うプレイ体験ができ、リプレイ性が高まります。また、プレイヤーがAIに指示を与え、ストーリーやキャラクターの行動を自動生成することもできます。例えば、NPCがプレイヤーの選択に基づいて予測不能な行動を取るなど。
3. 時間操作をテーマにしたパズルアクションゲーム
プレイヤーが時間を操作できる能力を持つキャラクターを操作し、時間を巻き戻したり、早送りしたりすることでパズルや敵を攻略していくアクションゲーム。ステージごとに異なるギミックや障害物があり、プレイヤーは正しいタイミングで時間操作を行いながらクリアしていく。たとえば、壊れた橋を渡るために過去に戻って橋が壊れる前の状態にしたり、未来を見て敵の動きを予測して戦うことが可能。
4. 未来都市でのハッキングアドベンチャーゲーム
近未来のディストピア都市を舞台に、プレイヤーはハッカーとして都市のセキュリティシステムを突破し、情報を集めて大企業や政府の陰謀を暴いていくアドベンチャーゲーム。プレイヤーはさまざまなツールやAIを駆使してデータをハッキングし、謎を解き明かしていく。サイバーパンクな世界観を持ち、ストーリー重視のゲームプレイが楽しめます。
5. サバイバル・クラフトゲーム:幻想世界編
ファンタジー世界を舞台にしたサバイバルゲーム。プレイヤーは広大なオープンワールドで資源を集めてクラフトしながら生き延びることが目的です。敵はモンスターだけでなく、自然環境もプレイヤーを脅かします。建物を作ったり、仲間と協力して拠点を強化したりする要素も組み込めます。季節や天候の変化がゲームの難易度に影響する仕組みも面白いかもしれません。
どのアイディアも、あなたの好みや開発中のプロジェクトに合わせてカスタマイズできます。興味のあるテーマがあれば、さらに詳しい設計やメカニクスについても一緒に考えられます。
あなた:
もっと
ChatGPT:
ChatGPT
さらにいくつかのゲーム開発アイデアを提案します。これらは、異なるジャンルやプレイスタイルに対応しており、どのような方向性でも採用できるように工夫しています。
6. ドリームメーカー:夢の中での冒険アドベンチャー
プレイヤーが夢の中の世界を探検し、様々な夢のシナリオを体験するアドベンチャーゲーム。プレイヤーは毎晩異なる夢の世界に入り込み、その中での問題を解決し、夢の断片を集めて現実世界に戻る方法を見つけます。夢の中では物理法則が歪み、奇妙な生き物や不思議なパズルに出会います。プレイヤーの選択に応じて夢の内容やエンディングが変わる、マルチエンディングシステムを導入。
7. 環境保護RPG:エコ・ヒーローズ
プレイヤーが環境保護のヒーローとして、地球を救うために自然災害や環境破壊と戦うRPG。プレイヤーは環境を守るための魔法やテクノロジーを使い、敵対する企業やモンスターと戦います。プレイヤーは森や海、山などの環境を守るためにクエストを遂行し、生態系を修復することが目標です。協力プレイやソロプレイが可能で、リアルタイムで環境が変化するシステムが特徴。
8. 時代を超えた戦士:タイムトラベルアクションゲーム
プレイヤーは時間を操る力を持つ戦士となり、過去や未来を旅しながら戦い続けるアクションゲーム。各時代には異なる敵や味方、そしてその時代特有の技術や武器が存在します。プレイヤーは異なる時代の武器や技術を組み合わせて戦略を練り、歴史を守りつつ、時間を破壊しようとする敵を打ち破ります。例:中世の剣術と未来のレーザーガンを同時に使用するなど、独特のコンバットシステムが楽しめます。
9. AIとの共存シミュレーションゲーム
プレイヤーはAIとの共存をテーマにした未来世界で生きる市民となり、日々の生活をシミュレーションします。AIが支配する都市で、人間とAIの関係性を選択していくゲーム。プレイヤーはAIと協力するか、反乱を起こすか、独自の方法で共存を図るかを選び、それぞれの選択が都市全体に影響を与えます。AIの発展を助けることで便利な都市生活が送れる一方で、過剰な技術依存が問題を引き起こすことも。プレイヤーの選択によりエンディングが変わる、リソース管理と戦略要素を持つシミュレーションです。
10. 音楽バトル:リズムに乗って戦うアクションゲーム
プレイヤーは音楽に合わせて戦うバトルアクションゲーム。敵の攻撃やプレイヤーのアクションが音楽と連動し、リズムに合わせてボタンを押すことでコンボを繋げます。敵を倒すたびにBGMが変化し、ステージごとに異なる音楽体験が楽しめます。プレイヤーがリズムをうまくつかむことで、攻撃力が増したり、特殊なアクションが発動したりします。さまざまな音楽ジャンル(ロック、エレクトロニカ、クラシックなど)を取り入れたステージが用意されます。
11. モンスター育成×バトルゲーム:育てて戦う生物シュミレーター
モンスターを育て、戦わせるシミュレーションゲーム。プレイヤーは様々な属性や能力を持つモンスターを捕まえ、育てて強化します。育成の過程ではトレーニングやアイテム、特定のイベントが重要になり、モンスターの成長によって異なる進化が可能です。戦闘システムは戦略性が高く、ターン制で各モンスターの特技や属性を駆使して戦うことになります。オンライン対戦や協力プレイも可能にすることで、コミュニティ要素も強化できます。
12. ホラー脱出ゲーム:闇の館
プレイヤーは恐ろしい館からの脱出を目指すホラーゲーム。ランダム生成される館の中で、プレイヤーは限られた時間内に謎を解き、脱出する必要があります。敵となる存在はプレイヤーを追い詰め、音や視覚を使ったトラップが待ち受けます。館内のアイテムを駆使し、適切な判断を下すことで生き残ることができます。さらに、マルチプレイヤーモードを追加し、複数人で協力しながら脱出するモードも楽しめます。
これらのアイディアも、プレイスタイルや技術レベルに応じてカスタマイズ可能です。どれかが気に入れば、その内容をさらに掘り下げて詳細な設計に移ることもできます。
ChatGPT の回答は必ずしも正しいとは限りません。重要な情報は確認するようにしてください。?
ChatGPT: さらにいくつかのゲーム開発アイデアを提案します。これらは、異なるジャンルやプレイスタイルに対応しており、どのような方向性でも採用できるように工夫しています。 ### 6. **ドリームメーカー:夢の中での冒険アドベンチャー** プレイヤーが夢の中の世界を探検し、様々な夢のシナリオを体験するアドベンチャーゲーム。プレイヤーは毎晩異なる夢の世界に入り込み、その中での問題を解決し、夢の断片を集めて現実世界に戻る方法を見つけます。夢の中では物理法則が歪み、奇妙な生き物や不思議なパズルに出会います。プレイヤーの選択に応じて夢の内容やエンディングが変わる、マルチエンディングシステムを導入。 ### 7. **環境保護RPG:エコ・ヒーローズ** プレイヤーが環境保護のヒーローとして、地球を救うために自然災害や環境破壊と戦うRPG。プレイヤーは環境を守るための魔法やテクノロジーを使い、敵対する企業やモンスターと戦います。プレイヤーは森や海、山などの環境を守るためにクエストを遂行し、生態系を修復することが目標です。協力プレイやソロプレイが可能で、リアルタイムで環境が変化するシステムが特徴。 ### 8. **時代を超えた戦士:タイムトラベルアクションゲーム** プレイヤーは時間を操る力を持つ戦士となり、過去や未来を旅しながら戦い続けるアクションゲーム。各時代には異なる敵や味方、そしてその時代特有の技術や武器が存在します。プレイヤーは異なる時代の武器や技術を組み合わせて戦略を練り、歴史を守りつつ、時間を破壊しようとする敵を打ち破ります。例:中世の剣術と未来のレーザーガンを同時に使用するなど、独特のコンバットシステムが楽しめます。 ### 9. **AIとの共存シミュレーションゲーム** プレイヤーはAIとの共存をテーマにした未来世界で生きる市民となり、日々の生活をシミュレーションします。AIが支配する都市で、人間とAIの関係性を選択していくゲーム。プレイヤーはAIと協力するか、反乱を起こすか、独自の方法で共存を図るかを選び、それぞれの選択が都市全体に影響を与えます。AIの発展を助けることで便利な都市生活が送れる一方で、過剰な技術依存が問題を引き起こすことも。プレイヤーの選択によりエンディングが変わる、リソース管理と戦略要素を持つシミュレーションです。 ### 10. **音楽バトル:リズムに乗って戦うアクションゲーム** プレイヤーは音楽に合わせて戦うバトルアクションゲーム。敵の攻撃やプレイヤーのアクションが音楽と連動し、リズムに合わせてボタンを押すことでコンボを繋げます。敵を倒すたびにBGMが変化し、ステージごとに異なる音楽体験が楽しめます。プレイヤーがリズムをうまくつかむことで、攻撃力が増したり、特殊なアクションが発動したりします。さまざまな音楽ジャンル(ロック、エレクトロニカ、クラシックなど)を取り入れたステージが用意されます。 ### 11. **モンスター育成×バトルゲーム:育てて戦う生物シュミレーター** モンスターを育て、戦わせるシミュレーションゲーム。プレイヤーは様々な属性や能力を持つモンスターを捕まえ、育てて強化します。育成の過程ではトレーニングやアイテム、特定のイベントが重要になり、モンスターの成長によって異なる進化が可能です。戦闘システムは戦略性が高く、ターン制で各モンスターの特技や属性を駆使して戦うことになります。オンライン対戦や協力プレイも可能にすることで、コミュニティ要素も強化できます。 ### 12. **ホラー脱出ゲーム:闇の館** プレイヤーは恐ろしい館からの脱出を目指すホラーゲーム。ランダム生成される館の中で、プレイヤーは限られた時間内に謎を解き、脱出する必要があります。敵となる存在はプレイヤーを追い詰め、音や視覚を使ったトラップが待ち受けます。館内のアイテムを駆使し、適切な判断を下すことで生き残ることができます。さらに、マルチプレイヤーモードを追加し、複数人で協力しながら脱出するモードも楽しめます。 これらのアイディアも、プレイスタイルや技術レベルに応じてカスタマイズ可能です。どれかが気に入れば、その内容をさらに掘り下げて詳細な設計に移ることもできます。
PHP 配列と新しい配列
<?php
$scores = [70, 90, 80];
$updatedScorfes = [];
foreach ($scores as $score){
$updatedScores[] = $score + 5;
}
print_r($updatedScores);
PHP 配列とforeach
<?php
$scores = [70, 90, 80];
foreach($scores as $key => $value){
echo “Score{{$key}}: {$value}” . PHP_EOL;
}
タスク管理.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AIベースのタスク管理アプリ</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f9;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
color: #333;
}
.task-container {
width: 90%;
max-width: 1200px;
padding: 20px;
}
.task-form {
display: flex;
flex-direction: column;
margin-bottom: 20px;
}
.task-form input, .task-form textarea {
margin-bottom: 10px;
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 5px;
}
.task-form button {
padding: 10px;
background-color: #28a745;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}
.task-form button:hover {
background-color: #218838;
}
.kanban-board {
display: flex;
justify-content: space-around;
}
.kanban-column {
background-color: #f8f9fa;
padding: 20px;
width: 30%;
border-radius: 10px;
min-height: 300px;
}
.kanban-column h2 {
text-align: center;
}
.task-list {
margin-top: 10px;
}
.task-item {
display: flex;
justify-content: space-between;
background-color: #e9ecef;
margin-bottom: 10px;
padding: 10px;
border-radius: 5px;
}
.task-item.high {
background-color: #ffcccc;
}
.task-item.medium {
background-color: #fff3cd;
}
.task-item.low {
background-color: #d4edda;
}
.task-item button {
background-color: #dc3545;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}
.task-item button:hover {
background-color: #c82333;
}
</style>
</head>
<body>
<h1>タスク管理</h1>
<div class="task-container">
<form class="task-form" id="taskForm">
<input type="text" id="taskTitle" placeholder="タスクタイトル" required>
<textarea id="taskDescription" placeholder="タスク詳細" rows="4"></textarea>
<input type="date" id="taskDueDate" required>
<button type="submit">タスクを追加</button>
</form>
<div class="kanban-board">
<div class="kanban-column" id="notStarted">
<h2>未着手</h2>
<div class="task-list" id="notStartedList"></div>
</div>
<div class="kanban-column" id="inProgress">
<h2>進行中</h2>
<div class="task-list" id="inProgressList"></div>
</div>
<div class="kanban-column" id="completed">
<h2>完了</h2>
<div class="task-list" id="completedList"></div>
</div>
</div>
</div>
<script>
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
// 優先度予測(ルールベース)
function predictPriority(taskDueDate) {
const daysLeft = (new Date(taskDueDate) - new Date()) / (1000 * 60 * 60 * 24);
if (daysLeft < 2) {
return 'High'; // 緊急度が高い
} else if (daysLeft < 7) {
return 'Medium'; // 1週間以内
} else {
return 'Low'; // 余裕がある
}
}
// タスクをローカルストレージに保存
function saveTasks() {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
// タスク表示
function loadTasks() {
const notStartedList = document.getElementById('notStartedList');
const inProgressList = document.getElementById('inProgressList');
const completedList = document.getElementById('completedList');
notStartedList.innerHTML = '';
inProgressList.innerHTML = '';
completedList.innerHTML = '';
tasks.forEach(task => {
const taskItem = document.createElement('div');
taskItem.classList.add('task-item', task.priority.toLowerCase());
taskItem.setAttribute('draggable', true);
taskItem.innerHTML = `
<div>
<strong>${task.title}</strong>
<p>${task.description}</p>
<small>期限: ${new Date(task.dueDate).toLocaleDateString()}</small>
<p>優先度: ${task.priority}</p>
</div>
<button onclick="deleteTask('${task.id}')">削除</button>
`;
taskItem.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', task.id);
});
if (task.status === 'notStarted') {
notStartedList.appendChild(taskItem);
} else if (task.status === 'inProgress') {
inProgressList.appendChild(taskItem);
} else if (task.status === 'completed') {
completedList.appendChild(taskItem);
}
});
}
// タスク追加
document.getElementById('taskForm').addEventListener('submit', (e) => {
e.preventDefault();
const title = document.getElementById('taskTitle').value;
const description = document.getElementById('taskDescription').value;
const dueDate = document.getElementById('taskDueDate').value;
const priority = predictPriority(dueDate);
const newTask = {
id: Date.now().toString(),
title,
description,
dueDate,
priority,
status: 'notStarted' // 初期状態は未着手
};
tasks.push(newTask);
saveTasks();
document.getElementById('taskForm').reset();
loadTasks();
});
// タスク削除
function deleteTask(taskId) {
tasks = tasks.filter(task => task.id !== taskId);
saveTasks();
loadTasks();
}
// ドラッグ&ドロップの設定
document.querySelectorAll('.kanban-column').forEach(column => {
column.addEventListener('dragover', (e) => {
e.preventDefault();
});
column.addEventListener('drop', (e) => {
const taskId = e.dataTransfer.getData('text/plain');
const newStatus = column.id;
const task = tasks.find(task => task.id === taskId);
task.status = newStatus;
saveTasks();
loadTasks();
});
});
// ページ読み込み時にタスクを表示
loadTasks();
</script>
</body>
</html>
宇宙開発のアイディア
宇宙開発に関連する革新的なアイデアをいくつか提案します。これらのアイデアは、技術の発展や社会的インパクトを考慮したものです。
1. 宇宙資源採掘プラットフォーム
小惑星や月などから資源(鉱物や水)を採掘し、地球や宇宙ステーションに供給するプロジェクトです。資源採掘ロボットや自動化された採掘プロセスを開発し、宇宙における持続可能な資源利用を促進します。これにより、宇宙開発がより経済的に自立する可能性があります。
2. 宇宙ゴミ除去ドローン
宇宙ゴミ(スペースデブリ)が増加する中、専用のドローンを開発し、宇宙空間でのゴミ除去を行うサービスを提供することが考えられます。人工衛星や宇宙ステーションの安全を守るため、宇宙ゴミの回収や処理を効率的に行うシステムを作ります。
3. 宇宙旅行向けホテルのモジュール式設計
宇宙旅行の需要が増えることを見越して、宇宙ホテルのモジュール式建設を提案します。小型の居住モジュールを宇宙空間に送り、これらを連結して大規模な宇宙ホテルを形成します。乗客は地球を周回しながら宇宙滞在を楽しめるようにします。
4. 宇宙インフラの遠隔制御システム
宇宙ステーションや月面基地など、宇宙インフラの運用を地球から遠隔で制御するシステムを開発します。AIやロボティクスを活用し、地球からのリモート操作で日常的なメンテナンスや修理を行い、人員の負担を軽減します。
5. 宇宙農業プロジェクト
宇宙における食料自給のため、月面や宇宙ステーション内で作物を栽培するプロジェクトを提案します。環境調整システムを用いて、極限環境でも食料を安定供給できる農業技術を確立し、将来的な火星移住計画などにも応用します。
6. 宇宙教育プラットフォーム
学生や宇宙ファンに向けた宇宙教育プラットフォームを開発します。VRやAR技術を活用し、宇宙の仮想体験を提供したり、リアルタイムで宇宙探査ミッションを追体験できる機能を提供します。また、専門知識を学べるオンラインコースを作成し、次世代の宇宙科学者やエンジニアの育成をサポートします。
7. 月面または火星のシミュレーションゲーム
月面や火星でのコロニー運営をシミュレーションするゲームを開発します。これにより、実際の宇宙開発に向けた社会的関心を高め、また、仮想空間で新しいアイデアや技術を試すことができます。ゲーム内でのデータやフィードバックは、リアルな宇宙開発における設計に活かすことができるでしょう。
8. 宇宙での医療リサーチ
微小重力環境を利用して、新しい医療技術や薬品の開発を行うプロジェクト。地球上では再現できない宇宙環境を活用し、病気の治療法や身体の回復メカニズムを研究します。宇宙での長期滞在をサポートするための健康管理システムも開発します。
これらのアイデアは、技術的進歩に伴う新たな挑戦や、宇宙開発における持続可能性の向上に貢献するものです。
PHP 配列の要素を変数に代入
<?php
// $scores = [70, 90, 80];
// $firstScore = $scores[0];
// $secondScore = $scores[1];
// $thirdScore = $scores[2];
// list($firstScore, $secondScore, $thirdScore) = $scores;
// [$firstScore, $secondScore, $thirdScore] = $scores;
// echo $firstScore . PHP_EOL;
// echo $secondScore . PHP_EOL;
// echo $thirdScore . PHP_EOL;
$x = 10;
$y = 20;
[$y, $x] = [$x, $y];
echo $x . PHP_EOL;
echo $y . PHP_EOL;