【スポンサーリンク】

[Processing] 簡易生態系シミュレーターを作っている

[Processing] 簡易生態系シミュレーターを作っている

生態系のシミュレーションを行うProcessingプログラムを作っています(途中)。

[Processing] 簡易生態系シミュレーターを作っている

たまにこういうのをずっと眺めたくなります。

\記事が役に立ったらシェアしてね/
【スポンサーリンク】

1. Procesingで作った

このシミュレーションは、複雑な生態系の相互作用をモデル化し、視覚的に表現しています。
パラメータを調整することで、様々な生態系の状況を観察できるようになっています。

Procesingで作った
ArrayList<Water> water;
ArrayList<Tree> trees;
ArrayList<Grass> grass;
ArrayList<Herbivore> herbivores;
ArrayList<Carnivore> carnivores;

// Parameters
int waterCount = 5;
int treeCount = 10;
int grassCount = 50;
int herbivoreCount = 20;
int carnivoreCount = 5;
float grassGrowthRate = 0.02;
float treeGrowthRate = 0.005;
float herbivoreSpeed = 2;
float carnivoreSpeed = 4;

// Season
int season = 0; // 0: Spring, 1: Summer, 2: Autumn, 3: Winter
int seasonDay = 0;
int seasonLength = 100;

// Population history
ArrayList<Integer> treeHistory = new ArrayList<Integer>();
ArrayList<Integer> grassHistory = new ArrayList<Integer>();
ArrayList<Integer> herbivoreHistory = new ArrayList<Integer>();
ArrayList<Integer> carnivoreHistory = new ArrayList<Integer>();

void setup() {
  size(1200, 800);
  resetSimulation();
}

void draw() {
  background(0);
  
  // Update season
  seasonDay++;
  if (seasonDay >= seasonLength) {
    season = (season + 1) % 4;
    seasonDay = 0;
  }
  
  // Apply seasonal effects
  applySeasonalEffects();
  
  // Update and display all elements
  updateAndDisplayElements();
  
  // Draw environment border
  noFill();
  stroke(255);
  rect(width * 0.2, height * 0.2, width * 0.6, height * 0.6);
  
  // Display current season and population counts
  displayInfo();
  
  // Update and display population history graph
  updatePopulationHistory();
  displayPopulationGraph();
}

void resetSimulation() {
  water = new ArrayList<Water>();
  trees = new ArrayList<Tree>();
  grass = new ArrayList<Grass>();
  herbivores = new ArrayList<Herbivore>();
  carnivores = new ArrayList<Carnivore>();
  
  for (int i = 0; i < waterCount; i++) {
    water.add(new Water(random(width * 0.6) + width * 0.2, random(height * 0.6) + height * 0.2));
  }
  for (int i = 0; i < treeCount; i++) {
    trees.add(new Tree(random(width * 0.6) + width * 0.2, random(height * 0.6) + height * 0.2));
  }
  for (int i = 0; i < grassCount; i++) {
    grass.add(new Grass(random(width * 0.6) + width * 0.2, random(height * 0.6) + height * 0.2));
  }
  for (int i = 0; i < herbivoreCount; i++) {
    herbivores.add(new Herbivore(random(width * 0.6) + width * 0.2, random(height * 0.6) + height * 0.2));
  }
  for (int i = 0; i < carnivoreCount; i++) {
    carnivores.add(new Carnivore(random(width * 0.6) + width * 0.2, random(height * 0.6) + height * 0.2));
  }
}

void applySeasonalEffects() {
  switch(season) {
    case 0: // Spring
      grassGrowthRate = 0.03;
      treeGrowthRate = 0.006;
      break;
    case 1: // Summer
      grassGrowthRate = 0.04;
      treeGrowthRate = 0.007;
      herbivoreSpeed = 2.5;
      carnivoreSpeed = 3.5;
      break;
    case 2: // Autumn
      grassGrowthRate = 0.02;
      treeGrowthRate = 0.004;
      break;
    case 3: // Winter
      grassGrowthRate = 0.01;
      treeGrowthRate = 0.002;
      herbivoreSpeed = 1.5;
      carnivoreSpeed = 2.5;
      break;
  }
}

void updateAndDisplayElements() {
  for (Water w : water) {
    w.affect();
    w.display();
  }
  
  for (int i = trees.size() - 1; i >= 0; i--) {
    Tree t = trees.get(i);
    t.grow();
    t.generateGrass();
    t.display();
    if (t.isDead()) {
      trees.remove(i);
    }
  }
  
  for (int i = grass.size() - 1; i >= 0; i--) {
    Grass g = grass.get(i);
    g.grow();
    g.display();
    if (g.isDead()) {
      grass.remove(i);
    }
  }
  
  for (int i = herbivores.size() - 1; i >= 0; i--) {
    Herbivore h = herbivores.get(i);
    h.move();
    h.eat();
    h.reproduce();
    h.display();
    if (h.isDead()) {
      herbivores.remove(i);
    }
  }
  
  for (int i = carnivores.size() - 1; i >= 0; i--) {
    Carnivore c = carnivores.get(i);
    c.move();
    c.hunt();
    c.reproduce();
    c.display();
    if (c.isDead()) {
      carnivores.remove(i);
    }
  }
}


void updatePopulationHistory() {
  treeHistory.add(trees.size());
  grassHistory.add(grass.size());
  herbivoreHistory.add(herbivores.size());
  carnivoreHistory.add(carnivores.size());
  
  // Keep only the last 200 data points
  if (treeHistory.size() > 200) {
    treeHistory.remove(0);
    grassHistory.remove(0);
    herbivoreHistory.remove(0);
    carnivoreHistory.remove(0);
  }
}

void displayInfo() {
  // 半透明の背景を描画
  fill(0, 0, 0, 150);  // 黒色で透明度150(0-255の範囲)
  noStroke();
  rect(width * 0.8 - 10, 10, width * 0.2 - 10, 160);

  fill(255, 255, 255, 200);  // 白色のテキストで透明度200
  textSize(16);
  String[] seasons = {"Spring", "Summer", "Autumn", "Winter"};
  text("Season: " + seasons[season], width * 0.8, 30);
  text("Trees: " + trees.size(), width * 0.8, 60);
  text("Grass: " + grass.size(), width * 0.8, 90);
  text("Herbivores: " + herbivores.size(), width * 0.8, 120);
  text("Carnivores: " + carnivores.size(), width * 0.8, 150);
}

void displayPopulationGraph() {
  float graphWidth = width * 0.6;
  float graphHeight = height * 0.2;
  float graphX = width * 0.2;
  float graphY = height * 0.85;
  
  // 半透明のグラフ背景を描画
  fill(50, 50, 50, 150);  // 暗灰色で透明度150
  noStroke();
  rect(graphX, graphY - graphHeight, graphWidth, graphHeight);
  
  // グラフの枠線を描画
  stroke(255, 255, 255, 200);  // 白色で透明度200
  noFill();
  rect(graphX, graphY - graphHeight, graphWidth, graphHeight);
  
  // Calculate max population
  int maxPopulation = 0;
  for (int i = 0; i < treeHistory.size(); i++) {
    maxPopulation = max(maxPopulation, treeHistory.get(i));
    maxPopulation = max(maxPopulation, grassHistory.get(i));
    maxPopulation = max(maxPopulation, herbivoreHistory.get(i));
    maxPopulation = max(maxPopulation, carnivoreHistory.get(i));
  }
  
  // Draw population lines
  drawPopulationLine(treeHistory, color(0, 128, 0, 200), graphX, graphY, graphWidth, graphHeight, maxPopulation);
  drawPopulationLine(grassHistory, color(0, 255, 0, 200), graphX, graphY, graphWidth, graphHeight, maxPopulation);
  drawPopulationLine(herbivoreHistory, color(255, 255, 0, 200), graphX, graphY, graphWidth, graphHeight, maxPopulation);
  drawPopulationLine(carnivoreHistory, color(255, 0, 0, 200), graphX, graphY, graphWidth, graphHeight, maxPopulation);
}

void drawPopulationLine(ArrayList<Integer> history, color c, float graphX, float graphY, float graphWidth, float graphHeight, int maxPopulation) {
  stroke(c);
  noFill();
  beginShape();
  for (int i = 0; i < history.size(); i++) {
    float x = map(i, 0, history.size() - 1, graphX, graphX + graphWidth);
    float y = map(history.get(i), 0, maxPopulation, graphY, graphY - graphHeight);
    vertex(x, y);
  }
  endShape();
}
// マウスクリックでシミュレーションをリセットする機能を追加
void mousePressed() {
  resetSimulation();
}
class Water {
  float x, y;
  float radius = 30;
  
  Water(float x, float y) {
    this.x = x;
    this.y = y;
  }
  
  void affect() {
    for (Grass g : grass) {
      if (dist(x, y, g.x, g.y) < radius * 2) {
        g.growthRate *= 3;
      } else if (dist(x, y, g.x, g.y) < radius * 5) {
        g.growthRate *= 1.3;
      }
    }
  }
  
  void display() {
    fill(0, 0, 255);
    ellipse(x, y, radius * 2, radius * 2);
  }
}

class Tree {
  float x, y;
  float size = 10;
  float maxSize = 40;
  float energy = 50;
  float maxEnergy = 100;
  float reproduceEnergy = 80;
  float grassGenerationThreshold = 70; // この閾値を超えるとgrassを生成
  float viewRadius = 10; // 木の「視野」範囲
  
  Tree(float x, float y) {
    this.x = x;
    this.y = y;
  }
  
  void grow() {

    if (energy > 0 && size < maxSize) {
      size += treeGrowthRate;
      energy += treeGrowthRate * 2;
    } else if (size >= maxSize && energy > reproduceEnergy) {
      reproduceTree();
      generateGrass();
    }

    
    energy = constrain(energy, 0, maxEnergy);
    size = constrain(size, 10, maxSize);
  }
  
  void reproduceTree() {
    if (random(1) < 0.05 && trees.size() < 100) {
      float reproductionChance = 0.05;
      if (countNearbyTrees() == 0) reproductionChance *= 2; // 近くに木がない場合、繁殖確率を2倍に
      
      if (random(1) < reproductionChance) {
        float newX = x + random(-100, 100);
        float newY = y + random(-100, 100);
        if (newX > width * 0.2 && newX < width * 0.8 && newY > height * 0.2 && newY < height * 0.8) {
          trees.add(new Tree(newX, newY));
          energy -= 30;
        }
      }
    }
  }
  
  void generateGrass() {
    int grassesToGenerate = int(random(1, 4)); // 1~3の草を生成
    for (int i = 0; i < grassesToGenerate; i++) {
      if (grass.size() < 200) {
        float gx = x + random(-100, 100);
        float gy = y + random(-100, 100);
        if (gx > width * 0.2 && gx < width * 0.8 && gy > height * 0.2 && gy < height * 0.8) {
          grass.add(new Grass(gx, gy));
          energy -= 5; // 草1つ生成するごとに10エネルギーを消費
        }
      }
    }
  }
  
  int countNearbyTrees() {
    int count = 0;
    for (Tree t : trees) {
      if (t != this && dist(x, y, t.x, t.y) < viewRadius) {
        count++;
      }
    }
    return count;
  }
  
  boolean isDead() {
    if (trees.size() <= 1) return false;
    int nearbyTrees = countNearbyTrees();
    return nearbyTrees > 10;
  }
  
  void display() {
    fill(0, 128, 0);
    ellipse(x, y, size, size);
  }
}

class Grass {
  float x, y;
  float size = 5;
  float maxSize = 15;
  float growthRate = grassGrowthRate;
  float energy = 20;
  float maxEnergy = 40;
  boolean isEaten = false;
  float viewRadius = 10; // 草の「視野」範囲
  
  Grass(float x, float y) {
    this.x = x;
    this.y = y;
  }
  
  void grow() {
    if (energy > 0 && size < maxSize) {
      size += growthRate;
      energy += growthRate * 2;
    } else if (size >= maxSize && energy > 30) {
      reproduceGrass();
    }
    energy = constrain(energy, 0, maxEnergy);
    size = constrain(size, 5, maxSize);
  }
  
  void reproduceGrass() {
    float reproductionChance = 0.05;
    if (countNearbyGrass() == 0) reproductionChance *= 2; // 近くに草がない場合、繁殖確率を2倍に
    
    if (random(1) < reproductionChance && grass.size() < 1000) {
      float newX = x + random(-30, 30);
      float newY = y + random(-30, 30);
      if (newX > width * 0.2 && newX < width * 0.8 && newY > height * 0.2 && newY < height * 0.8) {
        grass.add(new Grass(newX, newY));
        energy -= 20;
      }
    }
  }
  
  int countNearbyGrass() {
    int count = 0;
    for (Grass g : grass) {
      if (g != this && dist(x, y, g.x, g.y) < viewRadius) {
        count++;
      }
    }
    return count;
  }
  
  boolean isDead() {
    return grass.size() > 10 && (isEaten || energy <= 0);
  }
  
  void display() {
    fill(0, 255, 0);
    ellipse(x, y, size, size);
  }
}

class Herbivore {
  float x, y;
  float size = 10;
  float maxSize = 20;
  float energy = 100;
  float maxEnergy = 200;
  float reproduceEnergy = 10;
  float speed = herbivoreSpeed;
  float viewRadius = 100;
  PVector direction;
  int movementDuration = 0;
  int maxMovementDuration = 20;
  int eatingPause = 10;
  int maxEatingPause = 3; // 食事後の停止フレーム数
  
  Herbivore(float x, float y) {
    this.x = x;
    this.y = y;
    newDirection();
  }
  
  void newDirection() {
    if (energy < maxEnergy * 0.5) {
      Grass nearestGrass = findNearestGrass();
      if (nearestGrass != null) {
        direction = PVector.sub(new PVector(nearestGrass.x, nearestGrass.y), new PVector(x, y)).normalize();
      } else {
        direction = PVector.random2D();
      }
    } else {
      direction = PVector.random2D();
    }
    
    Carnivore nearestCarnivore = findNearestCarnivore();
    if (nearestCarnivore != null) {
      PVector escapeDirection = PVector.sub(new PVector(x, y), new PVector(nearestCarnivore.x, nearestCarnivore.y)).normalize();
      direction.add(escapeDirection.mult(1.5));
    }
    
    direction.normalize().mult(speed);
    movementDuration = 0;
  }
  
  void move() {
    if (eatingPause > 0) {
      eatingPause--;
      return; // 食事中は移動せずエネルギー消費もなし
    }
    
    if (movementDuration >= maxMovementDuration || random(1) < 0.02) {
      newDirection();
    }
    
    x += direction.x;
    y += direction.y;
    
    x = constrain(x, width * 0.2, width * 0.8);
    y = constrain(y, height * 0.2, height * 0.8);
    energy -= 0.5;
    movementDuration++;
  }
  
  Grass findNearestGrass() {
    Grass nearest = null;
    float minDist = viewRadius;
    for (Grass g : grass) {
      float d = dist(x, y, g.x, g.y);
      if (d < minDist) {
        minDist = d;
        nearest = g;
      }
    }
    return nearest;
  }
  
  Carnivore findNearestCarnivore() {
    Carnivore nearest = null;
    float minDist = viewRadius;
    for (Carnivore c : carnivores) {
      float d = dist(x, y, c.x, c.y);
      if (d < minDist) {
        minDist = d;
        nearest = c;
      }
    }
    return nearest;
  }
  
  void eat() {
    if (eatingPause > 0) return; // 既に食事中なら何もしない
    
    for (int i = grass.size() - 1; i >= 0; i--) {
      Grass g = grass.get(i);
      if (dist(x, y, g.x, g.y) < 10) {
        grass.remove(i);
        energy += 20;
        if (size < maxSize) size += 0.1;
        eatingPause = maxEatingPause; // 食事後の停止を開始
        break;
      }
    }
    energy = constrain(energy, 0, maxEnergy);
  }
   
  void reproduce() {
    if (energy > reproduceEnergy*2 && random(1) < 0.05 && herbivores.size() < 200) {
      herbivores.add(new Herbivore(x, y));
      energy = reproduceEnergy;
      eatingPause = maxEatingPause*10;
    }
  }
  
  boolean isDead() {
    return herbivores.size() > 1 && energy <= 0;
  }
  
  void display() {
    fill(255, 255, 0);
    ellipse(x, y, size, size);
    if (eatingPause > 0) {
      // 食事中であることを示す表示(例: 小さな緑の円)
      fill(0, 255, 0);
      ellipse(x, y, size / 2, size / 2);
    }
  }
}

class Carnivore {
  float x, y;
  float size = 15;
  float maxSize = 30;
  float energy = 150;
  float maxEnergy = 300;
  float reproduceEnergy = 100;
  float speed = carnivoreSpeed;
  float viewRadius = 300;
  PVector direction;
  int movementDuration = 0;
  int maxMovementDuration = 60;
  int eatingPause = 10;
  int maxEatingPause = 45; // 食事後の停止フレーム数(草食動物より長め)
  
  Carnivore(float x, float y) {
    this.x = x;
    this.y = y;
    newDirection();
  }
  
  void newDirection() {
    if (energy < maxEnergy * 0.5) {
      Herbivore nearestHerbivore = findNearestHerbivore();
      if (nearestHerbivore != null) {
        direction = PVector.sub(new PVector(nearestHerbivore.x, nearestHerbivore.y), new PVector(x, y)).normalize();
      } else {
        direction = PVector.random2D();
      }
    } else {
      direction = PVector.random2D();
    }
    
    direction.normalize().mult(speed);
    movementDuration = 0;
  }
  
  void move() {
    if (eatingPause > 0) {
      eatingPause--;
      return; // 食事中は移動せずエネルギー消費もなし
    }
    
    if (movementDuration >= maxMovementDuration || random(1) < 0.02) {
      newDirection();
    }
    
    float newX = x + direction.x;
    float newY = y + direction.y;
    
    // 木との衝突チェック
    boolean canMove = true;
    for (Tree tree : trees) {
      if (dist(newX, newY, tree.x, tree.y) < (size / 2 + tree.size / 2)) {
        canMove = false;
        break;
      }
    }
    
    if (canMove) {
      x = newX;
      y = newY;
    } else {
      newDirection();
    }
    
    x = constrain(x, width * 0.2, width * 0.8);
    y = constrain(y, height * 0.2, height * 0.8);
    energy -= 1;
    movementDuration++;
  }
   
  Herbivore findNearestHerbivore() {
    Herbivore nearest = null;
    float minDist = viewRadius;
    for (Herbivore h : herbivores) {
      float d = dist(x, y, h.x, h.y);
      if (d < minDist) {
        // 木を通過せずに到達できるかチェック
        boolean canReach = true;
        for (Tree tree : trees) {
          if (lineIntersectsCircle(x, y, h.x, h.y, tree.x, tree.y, tree.size / 2)) {
            canReach = false;
            break;
          }
        }
        if (canReach) {
          minDist = d;
          nearest = h;
        }
      }
    }
    return nearest;
  }
  
  boolean lineIntersectsCircle(float x1, float y1, float x2, float y2, float cx, float cy, float r) {
    float dx = x2 - x1;
    float dy = y2 - y1;
    float a = dx * dx + dy * dy;
    float b = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
    float c = cx * cx + cy * cy + x1 * x1 + y1 * y1 - 2 * (cx * x1 + cy * y1) - r * r;
    float discriminant = b * b - 4 * a * c;
    return discriminant >= 0;
  }
  
  void hunt() {
    if (eatingPause > 0) return; // 既に食事中なら何もしない
    
    for (int i = herbivores.size() - 1; i >= 0; i--) {
      Herbivore h = herbivores.get(i);
      if (dist(x, y, h.x, h.y) < 20) {
        herbivores.remove(i);
        energy += 50;
        if (size < maxSize) size += 0.2;
        eatingPause = maxEatingPause; // 食事後の停止を開始
        break;
      }
    }
    energy = constrain(energy, 0, maxEnergy);
  }
  
  
  void reproduce() {
    if (energy > reproduceEnergy && random(1) < 0.03 && carnivores.size()*10 < herbivores.size()) {
      carnivores.add(new Carnivore(x, y));
      energy -= 75;
      eatingPause = 30;
    }
  }
  
  boolean isDead() {
    return carnivores.size() > 1 && energy <= 0;
  }
  
  void display() {
    fill(255, 0, 0);
    ellipse(x, y, size, size);
    if (eatingPause > 0) {
      // 食事中であることを示す表示(例: 小さな黄色の円)
      fill(255, 255, 0);
      ellipse(x, y, size / 2, size / 2);
    }
  }
}
Procesingで作った

たった1つのファイルのコードだけでできています。

今回は、学習コードが多そうなJava版にしました。
Pythonモードだとエラーも多かったです。

1-1. シミュレーターの特徴

主な特徴は以下の通りです:

  1. 水、木、草、草食動物(Herbivore)、肉食動物(Carnivore)の5つの要素で構成されています。
  2. 季節の変化があり、各季節で生物の成長率や動物の速度が変化します。
  3. 各生物はエネルギーを持ち、成長や行動によってエネルギーを消費・回復します。
  4. 木は成長し、一定条件下で草を生成します。
  5. 草食動物は草を食べ、肉食動物は草食動物を捕食します。
  6. 動物は視野範囲内で餌を探し、捕食者から逃げる行動をとります。
  7. 肉食動物は木を通り抜けられません。
  8. 動物は食事後、一時的に移動を停止します。
  9. 画面右上に各生物の数を表示し、下部にグラフで個体数の推移を表示します。
  10. マウスクリックでシミュレーションをリセットできます。

1-2. 処理の大まかな流れ

各生物クラス(Water, Tree, Grass, Herbivore, Carnivore)は、それぞれ独自の行動ロジックを持っており、シミュレーション内で相互に影響し合います。
この循環的な処理により、動的な生態系シミュレーションが実現されています。

初期化 (setup関数):

  • ウィンドウサイズの設定
  • シミュレーションのリセット(各生物の初期配置)

メインループ (draw関数):

  • 背景の描画
  • 季節の更新と季節効果の適用
  • 全ての生物の更新と描画
  • 環境の境界線の描画
  • 情報表示(現在の季節と各生物の個体数)
  • 個体数履歴の更新とグラフ表示

生物の更新 (updateAndDisplayElements関数):

  • 水の影響を適用
  • 木の成長、草の生成、表示、死亡判定
  • 草の成長、表示、死亡判定
  • 草食動物の移動、摂食、繁殖、表示、死亡判定
  • 肉食動物の移動、狩猟、繁殖、表示、死亡判定

イベント処理:

  • マウスクリック時にシミュレーションをリセット

2. Claudeに与えた初期プロンプト

今回は、Claude 3.5 Sonnetで雛形を作りました。

生態系シミュレーターをシミュレーターをProcessingで作成してください。

水は
・動物が通れない
・植物は水の近くほど増えやすい
・近くにゆっくり下草を生産する

樹木
・成長はゆっくり
・近くに少しずつ下草を生産する
・草食動物も食べられない
・樹木が近くに多すぎると共倒れする

下草
・草食動物に食べられる
・成長は早い
・ある程度成長すると樹木になる

草食動物
・移動する
・動くたびに栄養を消費し、不足すると餓死する
・下草を見つけると食べ、栄養を得る
・ある程度、成長すると交尾して、繁殖する

肉食動物
・移動する
・動くたびに栄養を消費し、不足すると餓死する
・草食動物を追いかけ、食べる
・ある程度、成長すると交尾して、繁殖する

Claudeに与えた初期プロンプト

伝えたイメージとはだいぶ違いますが、なんとなくそれっぽいものが30分ほどの微調整で形になるのが嬉しいです。

Claudeに与えた初期プロンプト
こちらもどうぞ。
Pythonで動くハッシュ可視化プログラムを作った
Pythonで動くハッシュ可視化プログラムを作った
Processingのハッシュ可視化プログラム Procesing(Pythonモード)で動くハッシュ可視化プログラムを作りました。 コードはPythonで記述しています。 完成したコード # ハッシュ可視化プログラム # グローバル変数 input_string = 'apple' current_char_index = 0 current_hash = 0 animation_state = 0 animation_progress = 0 box_size = 50 ...

子どもにとってのViscuitの魅力(プログラミング学習)
子どもにとってのViscuitの魅力(プログラミング学習)
小学3年生の子どもがプログラミングに興味を示したので、3つの候補(Scratch、Processing、Viscuit)を見せてみました。 子どもが最も興味を示したのは Viscuit。 ルールの小さな変更が大きな変化をもたらすダイナミックさが子どもにとって面白いようで、夢中で遊んでいます。 「自分で作ったルールによって動く世界」。思い通りにならないことも学びにつながるのかも。 YouTube動画でも話しています。 プログラミング言語を選ぶ 最近、小学3年生の子どもが「パソコ...

Claudeの新機能Artifactsを表示できるようにした(Claude 3.5 Sonnet, Feature Preview)
Claudeの新機能Artifactsを表示できるようにした(Claude 3.5 Sonnet, Feature Preview)
Anthropicが新しい人工知能モデル「Claude 3.5 Sonnet」を発表し、その主要機能を紹介しました。 新機能「Artifacts」を使うと、AIが生成したコンテンツをリアルタイムで確認・編集できるようになりました。 Artifactsを表示するには、claude.aiのメニューにある「Feature Preview」から有効にします。 「Claude 3.5 Sonnet is here」というメール Anthropicから「Claude 3.5 Sonnet...
QRコードを読み込むと、関連記事を確認できます。

[Processing] 簡易生態系シミュレーターを作っている
【スポンサーリンク】
タイトルとURLをコピーしました