一、说明
本章介绍全部由AI生成的玩家,完成狼人杀游戏。做这个目的纯属爱好,自从了解了一些AI相关的技术后,总想着做点有趣的东西,一边练技术,一边打发时间吧~~
采用的前后分离开发模式:
后端技术:Spring AI、Spring AI alibaba、SpringBoot、Mysql、Redis、SSE等等
前端技术:VUE、ElementUI等等
目前后端基本完工,正在开发前端页面中....
准备过年在家写完~~
当然,游戏还有很多小逻辑并不完善,后面会慢慢修改,也欢迎有兴趣的同学们一起探讨~~
二、代码
1.POM文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.5</version>
<relativePath/>
</parent>
<groupId>com.zhan.weki</groupId>
<artifactId>werewolf-kill</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>werewolf-kill</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
<spring-ai.version>1.0.0</spring-ai.version>
<spring-ai-alibaba.version>1.0.0.2</spring-ai-alibaba.version>
<spring-boot.version>3.4.5</spring-boot.version>
<mybatis-plus.version>3.5.11</mybatis-plus.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-memory-redis</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!-- 添加 Redis 向量数据库依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-redis</artifactId>
<version>1.0.0</version>
</dependency>
<!-- MySQL 驱动(完整正确配置) -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
<version>8.0.33</version> <!-- 固定版本,避免版本适配问题 -->
</dependency>
<!--mybatis-plus 持久层-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- SpringDoc OpenAPI 3.0 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<!-- 支持 Java 8 时间类型(LocalDateTime/LocalDate 等)的序列化/反序列化 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<!-- Spring Boot 父工程已管理版本,无需手动指定 -->
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</build>
</project>
2.SQL脚本
-- 房间表
CREATE TABLE `room` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '房间ID',
`room_name` varchar(50) NOT NULL COMMENT '房间名称',
`player_count` int NOT NULL COMMENT '玩家数量',
`game_stage` varchar(20) NOT NULL COMMENT '游戏阶段',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
`status` int NOT NULL COMMENT '房间状态:0-未开始 1-进行中 2-已结束',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='狼人杀房间表';
-- AI玩家表
CREATE TABLE `ai_player` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '玩家ID',
`room_id` bigint NOT NULL COMMENT '房间ID',
`player_no` int NOT NULL COMMENT '玩家编号(1-12)',
`role` varchar(20) NOT NULL COMMENT '角色',
`ai_model_code` varchar(50) NOT NULL COMMENT '绑定的AI模型ID',
`status` varchar(20) NOT NULL COMMENT '状态:ALIVE-存活 DEAD-死亡',
`last_speak` text COMMENT '最后发言内容',
PRIMARY KEY (`id`),
KEY `idx_room_id` (`room_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI玩家表';
3.yml主配置文件
server:
port: 8080
application:
name: werewolf-kill
spring:
profiles:
active: dev
4.yml开发配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/gizz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: xxxxxxxxxxxxxx
# Redis配置(集群)
data:
redis:
host: xxxxxxxxxxxxxx
port: 6379
timeout: 3000
database: 0
password: xxxxxxxxxxxxxx
jedis:
pool:
max-active: 20
max-idle: 10
min-idle: 5
ai:
dashscope:
api-key: sk-09c7b571687b46d5a2e25a03fbddxxxx
client:
connection-timeout: 30000
read-timeout: 180000
call-timeout: 240000
retry:
max-attempts: 10
backoff:
initial-interval: 5000ms
multiplier: 1.5
# MyBatis-Plus配置
mybatis-plus:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
file:
name: wolf.log
path: logs
logback:
rollingPolicy:
maxFileSize: 100MB
maxHistory: 30
5.config配置类
package com.zhan.game.config;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @author zhanxuewei
*/
@Configuration
public class ChatClientFactory {
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
// 缓存已创建的 ChatClient 实例
private final Map<String, ChatClient> chatClientCache = new HashMap<>();
/**
* 根据模型代码构建对应的 ChatClient
*
* @param modelCode 模型代码
* @return 对应的 ChatClient 实例
*/
public ChatClient buildChatClient(String modelCode) {
// 检查缓存中是否存在
if (chatClientCache.containsKey(modelCode)) {
return chatClientCache.get(modelCode);
}
// 创建新的 ChatClient 并缓存
ChatClient chatClient = createChatClient(modelCode);
chatClientCache.put(modelCode, chatClient);
return chatClient;
}
/**
* 创建 ChatClient 实例
*
* @param modelCode 模型代码
* @return ChatClient 实例
*/
private ChatClient createChatClient(String modelCode) {
DashScopeChatModel chatModel = DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(modelCode).build())
.build();
return ChatClient.builder(chatModel)
.defaultOptions(ChatOptions.builder().model(modelCode).build())
.build();
}
}
6.接口文档配置
package com.zhan.game.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Knife4jConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title("API接口文档")
.description("API接口文档")
.version("1.0.0")
.contact(new Contact()
.name("wolf")
.email("603085899@qq.com")));
}
}
7.Redis配置
package com.zhan.game.config;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// ========== 1. Key序列化(统一为字符串,无任何冲突) ==========
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// ========== 2. Value序列化(支持所有类型,自动处理类型ID) ==========
GenericJackson2JsonRedisSerializer jacksonSerializer = new GenericJackson2JsonRedisSerializer(buildObjectMapper() // 自定义ObjectMapper,解决类型ID解析问题
);
redisTemplate.setValueSerializer(jacksonSerializer);
redisTemplate.setHashValueSerializer(jacksonSerializer);
// ========== 3. 初始化模板 ==========
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 构建ObjectMapper,解决「Could not resolve type id」问题
*/
private ObjectMapper buildObjectMapper() {
ObjectMapper om = new ObjectMapper();
// 1. 开启类型信息存储(序列化时记录类型,反序列化时识别)
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, // 放宽类型校验(兼容简单类型如String/Integer)
ObjectMapper.DefaultTyping.NON_FINAL, // 仅为非final类添加类型ID(避免String/Integer等基础类型报错)
JsonTypeInfo.As.PROPERTY // 类型ID作为属性存储(如@class: java.lang.String)
);
// 2. 其他配置(可选,提升兼容性)
om.enableDefaultTyping();
om.findAndRegisterModules(); // 注册所有Jackson模块(如Java8时间类型)
return om;
}
}
8.常量
package com.zhan.game.constants;
public interface ApiConstants {
public static final String API_PREFIX = "/api/v1";
}
9.常量
package com.zhan.game.constants;
public interface GameConstants {
String DEAD = "DEAD";
String ALIVE = "ALIVE";
int DEFAULT_PLAYER_COUNT = 12;
int PLAYER_NO_START_INDEX = 1;
int ROOM_STATUS_ACTIVE = 1;
}
10.统一接口返回封装
package com.zhan.game.common;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
/**
* 通用响应对象
*/
public class ApiResult<T> implements Serializable {
private static final long serialVersionUID = 899231L;
private Long code;
private String message;
private T data;
protected ApiResult() {
}
/**
* 构造响应对象
*
* @param code 状态吗
* @param message 返回信息
* @param data 返回数据
*/
public ApiResult(Long code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
/**
* 响应成功结果
*
* @param <T> 范形对象
* @return 返回范形对象
*/
public static <T> ApiResult<T> success() {
return new ApiResult<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), null);
}
/**
* 响应成功结果
*
* @param code 状态吗
* @param message 返回消息
* @param <T> 范形对象
* @return 范形对象
*/
public static <T> ApiResult<T> success(Long code, String message) {
return new ApiResult<>(code, message, null);
}
/**
* 响应成功结果
*
* @param data
* @param <T>
* @return
*/
public static <T> ApiResult<T> success(T data) {
return new ApiResult<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
}
/**
* 响应成功结果
*
* @param code
* @param message
* @param data
* @param <T>
* @return
*/
public static <T> ApiResult<T> success(Long code, String message, T data) {
return new ApiResult<>(code, message, data);
}
/**
* 响应成功结果
*
* @param message
* @param data
* @param <T>
* @return
*/
public static <T> ApiResult<T> success(String message, T data) {
return new ApiResult<>(ResultCode.SUCCESS.getCode(), message, data);
}
/**
* 响应成功结果
*
* @param errorCode
* @param data
* @param <T>
* @return
*/
public static <T> ApiResult<T> success(IErrorCode errorCode, T data) {
return new ApiResult<>(errorCode.getCode(), errorCode.getMessage(), data);
}
/**
* 响应成功结果
*
* @param errorCode
* @param <T>
* @return
*/
public static <T> ApiResult<T> success(IErrorCode errorCode) {
return success(errorCode, null);
}
/**
* 失败返回结果
*
* @param <T>
* @return
*/
public static <T> ApiResult<T> failed() {
return new ApiResult<>(ResultCode.FAILED.getCode(), ResultCode.FAILED.getMessage(), null);
}
/**
* 失败返回结果
*
* @param code
* @param message
* @param <T>
* @return
*/
public static <T> ApiResult<T> failed(Long code, String message) {
return new ApiResult<>(code, message, null);
}
/**
* 失败返回结果
*
* @param message
* @param <T>
* @return
*/
public static <T> ApiResult<T> failed(String message) {
return new ApiResult<>(ResultCode.FAILED.getCode(), message, null);
}
/**
* 失败返回结果
*
* @param errorCode
* @param <T>
* @return
*/
public static <T> ApiResult<T> failed(IErrorCode errorCode) {
return new ApiResult<>(errorCode.getCode(), errorCode.getMessage(), null);
}
/**
* 失败返回结果
*
* @param errorCode
* @param data
* @param <T>
* @return
*/
public static <T> ApiResult<T> failed(IErrorCode errorCode, T data) {
return new ApiResult<>(errorCode.getCode(), errorCode.getMessage(), data);
}
/**
* 失败返回结果
*
* @param message
* @param data
* @param <T>
* @return
*/
public static <T> ApiResult<T> failed(String message, T data) {
return new ApiResult<>(ResultCode.FAILED.getCode(), message, data);
}
public long getCode() {
return code;
}
public void setCode(long code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "CommonResult{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
11.错误模型接口
package com.zhan.game.common;
/**
* 错误模型接口
*/
public interface IErrorCode {
// 状态码
Long getCode();
// 错误消息
String getMessage();
}
12.返回结果枚举
package com.zhan.game.common;
/**
* 枚举了一些常用API操作码
*/
public enum ResultCode implements IErrorCode {
SUCCESS(200L, "操作成功"),
FAILED(500L, "操作失败"),
INVALID_ERROR(10005L, "参数格式校验失败"),
PARAMETER_TYPE_ERROR(10006L, "参数类型错误"),
OTHER_ERROR(10007L, "未知异常"),
FILE_SIZE_TOO_BIG_ERROR(10008L, "上传文件大小超出限制"),
ROOM_NOT_FOUND(2000L, "房间不存在"),
GAME_OVER(2001L, "游戏已结束"),
ADVANCE_GAME_STAGE_ERROR(2002L, "推进游戏阶段失败"),
ROOM_NO_PLAYER(2003L, "房间内无玩家"),
PLAYER_NOT_FOUND(2004L, "玩家编号不存在"),
GET_AI_SPEAK_RECORD_ERROR(2005L, "获取发言失败"),
GET_GAME_STATUS_ERROR(2006L, "获取状态失败"),
PLAYER_DEAD_OR_NOT_FOUND(2007L, "玩家不存在或已死亡"),
TRIGGER_AI_SPEAK_ERROR(2008L, "触发AI发言失败"),
ROLE_NOT_FOUND(2009L, "角色不存在"),
ROOM_EXIST(2010L, "房间已存在"),
ROOM_CREATION_BUSY(2011L, "房间创建繁忙,请稍后再试"),
;
private final Long code;
private final String message;
ResultCode(Long code, String message) {
this.code = code;
this.message = message;
}
@Override
public Long getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
13.控制器
package com.zhan.game.controller;
import com.zhan.game.common.ApiResult;
import com.zhan.game.constants.ApiConstants;
import com.zhan.game.entity.AiPlayer;
import com.zhan.game.entity.Room;
import com.zhan.game.service.AiPlayerService;
import com.zhan.game.service.GameService;
import com.zhan.game.vo.GameStageAdvanceVO;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping(ApiConstants.API_PREFIX + "/game")
@RequiredArgsConstructor
public class WerewolfController {
@Resource
private GameService gameService;
@Resource
private AiPlayerService aiPlayerService;
/**
* 创建狼人杀房间
*
* @param request 请求参数(roomName)
* @return 房间信息
*/
@PostMapping("/room/create")
public ApiResult<Room> createRoom(@RequestBody Map<String, String> request) {
String roomName = request.get("roomName");
Room room = gameService.createRoom(roomName);
return ApiResult.success(room);
}
/**
* 查询房间内所有AI玩家
*
* @param roomId 房间ID
* @return 玩家列表
*/
@GetMapping("/room/players/{roomId}")
public ApiResult<List<AiPlayer>> getRoomPlayers(@PathVariable Long roomId) {
return ApiResult.success(aiPlayerService.getRoomPlayers(roomId));
}
// ========== 新增核心接口 ==========
/**
* 推进游戏阶段(核心接口:触发夜晚/白天流程)
* 调用逻辑:创建房间后,调用此接口推进游戏,自动完成当前阶段的所有AI行动
*
* @param roomId 房间ID
* @return 阶段推进结果(包含当前阶段、AI行动概要)
*/
@PostMapping("/room/advance/{roomId}")
public ApiResult<GameStageAdvanceVO> advanceGameStage(@PathVariable Long roomId) {
GameStageAdvanceVO result = gameService.advanceGameStage(roomId);
return ApiResult.success(result);
}
/**
* 获取AI玩家的发言/行动记录(核心:查看AI对话内容)
*
* @param roomId 房间ID
* @param playerNo 玩家编号(可选,不传则返回所有AI的发言)
* @return 发言记录
*/
@GetMapping("/room/player/speak/{roomId}")
public ApiResult<List<AiPlayer>> getAiSpeakRecord(@PathVariable Long roomId, @RequestParam(required = false) Integer playerNo) {
List<AiPlayer> record = gameService.getAiSpeakRecord(roomId, playerNo);
return ApiResult.success(record);
}
/**
* 获取房间当前游戏状态(全局视角)
*
* @param roomId 房间ID
* @return 游戏状态(阶段、存活/死亡玩家、胜负结果等)
*/
@GetMapping("/room/status/{roomId}")
public ApiResult<Map<String, Object>> getGameStatus(@PathVariable Long roomId) {
Map<String, Object> result = gameService.getGameStatus(roomId);
return ApiResult.success(result);
}
/**
* 手动触发单个AI玩家发言(测试用,可选)
*
* @param roomId 房间ID
* @param playerNo 玩家编号
* @param prompt 自定义发言提示词(可选,不传则用默认提示词)
* @return 玩家发言内容
*/
@PostMapping("/room/player/speak/{roomId}/{playerNo}")
public ApiResult<Map<String, Object>> triggerAiSpeak(@PathVariable Long roomId,
@PathVariable Integer playerNo,
@RequestParam(required = false) String prompt) {
Map<String, Object> result = gameService.triggerAiSpeak(roomId, playerNo, prompt);
return ApiResult.success(result);
}
}
14.实体类
package com.zhan.game.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("ai_player")
public class AiPlayer implements Serializable {
@TableId(type = IdType.AUTO)
private Long id; // 玩家ID
private Long roomId; // 房间ID
private Integer playerNo; // 玩家编号(1-12)
private String role; // 角色(关联RoleEnum)
private String aiModelCode; // 绑定的AI模型ID
private String status; // 状态:ALIVE-存活 DEAD-死亡
private String lastSpeak; // 最后发言内容
}
package com.zhan.game.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@TableName("room")
public class Room implements Serializable {
@TableId(type = IdType.AUTO)
private Long id; // 房间ID
private String roomName; // 房间名称
private Integer playerCount; // 玩家数量(固定12)
private String gameStage; // 游戏阶段(关联GameStageEnum)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createTime; // 创建时间
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime updateTime; // 更新时间
private Integer status; // 房间状态:0-未开始 1-进行中 2-已结束
}
15.游戏相关的枚举
package com.zhan.game.enums;
public enum GameResult {
CONTINUE,
WOLF_WIN,
GOOD_WIN
}
package com.zhan.game.enums;
import lombok.Getter;
@Getter
public enum GameStageEnum {
NIGHT("夜晚阶段", 1), // 狼人杀人、预言家验人、女巫用药、守卫守护
DAY("白天阶段", 2), // 发言、投票
END("游戏结束", 3);
private final String name;
private final int order;
GameStageEnum(String name, int order) {
this.name = name;
this.order = order;
}
}
package com.zhan.game.enums;
import lombok.Getter;
@Getter
public enum ModelEnum {
WEREWOLF("狼人", "deepseek-v3.2"),
CIVILIAN("平民", "qwen3-max-preview"),
SEER("预言家", "deepseek-v3.2"),
WITCH("女巫", "deepseek-v3.2"),
HUNTER("猎人", "deepseek-v3.2"),
GUARD("守卫", "deepseek-v3.2"),
;
private final String name;
private final String code;
ModelEnum(String name, String code) {
this.name = name;
this.code = code;
}
public static ModelEnum getModeCodeByName(String name) {
for (ModelEnum modelEnum : ModelEnum.values()) {
if (modelEnum.getName().equals(name)) {
return modelEnum;
}
}
return ModelEnum.WEREWOLF;
}
}
package com.zhan.game.enums;
import lombok.Getter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Getter
public enum RoleEnum {
WEREWOLF("狼人", 4),
CIVILIAN("平民", 4),
SEER("预言家", 1),
WITCH("女巫", 1),
HUNTER("猎人", 1),
GUARD("守卫", 1);
private final String name;
private final int count; // 12人局该角色数量
RoleEnum(String name, int count) {
this.name = name;
this.count = count;
}
// 获取12人局所有角色列表(按数量生成)
public static List<RoleEnum> getFullRoles() {
List<RoleEnum> roles = new ArrayList<>();
for (RoleEnum role : RoleEnum.values()) {
for (int i = 0; i < role.getCount(); i++) {
roles.add(role);
}
}
// 随机打乱顺序
Collections.shuffle(roles);
return roles;
}
public static RoleEnum getRoleByName(String name) {
for (RoleEnum roleEnum : RoleEnum.values()) {
if (roleEnum.getName().equals(name)) {
return roleEnum;
}
}
return null;
}
}
package com.zhan.game.enums;
import lombok.Getter;
/**
* 女巫药水状态枚举
*/
@Getter
public enum WitchStatusEnum {
ANTIDOTE_AVAILABLE("解药可用", 1),
ANTIDOTE_USED("解药已用", 0),
POISON_AVAILABLE("毒药可用", 1),
POISON_USED("毒药已用", 0);
private final String desc;
private final int status;
WitchStatusEnum(String desc, int status) {
this.desc = desc;
this.status = status;
}
}
16.Mapper类
package com.zhan.game.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zhan.game.entity.AiPlayer;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface AiPlayerMapper extends BaseMapper<AiPlayer> {
}
package com.zhan.game.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zhan.game.entity.Room;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RoomMapper extends BaseMapper<Room> {
}
17.Service接口
package com.zhan.game.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zhan.game.entity.Room;
public interface RoomService extends IService<Room> {
void updateRoomStatus(Long roomId, int status);
void updateRoomStage(Long roomId, String name);
}
package com.zhan.game.service;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public interface RedisService {
// ========== 基础对象操作 ==========
<T> void setObject(String key, T value);
<T> void setObject(String key, T value, long timeout, TimeUnit unit);
@SuppressWarnings("unchecked")
<T> T getObject(String key, Class<T> clazz);
// ========== List操作 ==========
<T> void listRightPush(String key, T value);
<T> void listRightPushAll(String key, List<T> values);
@SuppressWarnings("unchecked")
<T> List<T> listRange(String key, long start, long end);
// ========== Hash操作 ==========
<T> void hashPut(String key, String hashKey, T value);
<T> void hashPutAll(String key, Map<String, T> map);
@SuppressWarnings("unchecked")
<T> T hashGet(String key, String hashKey, Class<T> clazz);
// ========== 通用操作 ==========
boolean hasKey(String key);
void deleteKey(String key);
/**
* 获取Hash的所有键值对(解决entries读取的序列化问题)
*
* @param key Redis的Hash Key
* @return 键值对Map(自动处理序列化/反序列化)
*/
Map<Object, Object> hashGetAllEntries(String key);
/**
* 设置键值对,仅当键不存在时才设置(分布式锁的基础操作)
* @param key 键
* @param value 值
* @param timeout 过期时间
* @param unit 时间单位
* @return 是否设置成功
*/
Boolean setIfAbsent(String key, Object value, long timeout, TimeUnit unit);
/**
* 删除键值对,仅当键存在且值相等时才删除(用于释放分布式锁)
* @param key 键
* @param value 值
* @return 是否删除成功
*/
Boolean deleteIfEquals(String key, Object value);
}
package com.zhan.game.service;
import com.zhan.game.entity.AiPlayer;
import com.zhan.game.entity.Room;
import com.zhan.game.enums.GameResult;
import java.util.List;
import java.util.Map;
public interface GameService {
Room createRoom(String roomName);
List<AiPlayer> getRoomPlayers(Long roomId);
List<AiPlayer> findDeadLastNight(Long roomId);
void clearNightDeadRecord(Long roomId);
void recordNightDeadPlayers(Long roomId, String killedByWolf, String poisonedByWitch, String guardTarget);
void nightStage(Long roomId);
void dayStage(Long roomId);
GameResult checkWinOrLose(Long roomId);
/**
* 推进游戏阶段(核心接口:触发夜晚/白天流程)
* 调用逻辑:创建房间后,调用此接口推进游戏,自动完成当前阶段的所有AI行动
*
* @param roomId 房间ID
* @return 阶段推进结果(包含当前阶段、AI行动概要)
*/
Map<String, Object> advanceGameStage(Long roomId);
/**
* 获取AI玩家的发言/行动记录(核心:查看AI对话内容)
*
* @param roomId 房间ID
* @param playerNo 玩家编号(可选,不传则返回所有AI的发言)
* @return 发言记录
*/
Map<String, Object> getAiSpeakRecord(Long roomId, Integer playerNo);
/**
* 获取房间当前游戏状态(全局视角)
*
* @param roomId 房间ID
* @return 游戏状态(阶段、存活/死亡玩家、胜负结果等)
*/
Map<String, Object> getGameStatus(Long roomId);
/**
* 手动触发单个AI玩家发言(测试用,可选)
*
* @param roomId 房间ID
* @param playerNo 玩家编号
* @param prompt 自定义发言提示词(可选,不传则用默认提示词)
* @return 玩家发言内容
*/
Map<String, Object> triggerAiSpeak(Long roomId, Integer playerNo, String prompt);
}
package com.zhan.game.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zhan.game.entity.AiPlayer;
import com.zhan.game.enums.RoleEnum;
import java.util.List;
public interface AiPlayerService extends IService<AiPlayer> {
String aiAction(AiPlayer player, String prompt);
List<AiPlayer> getRoomPlayers(Long roomId);
void initWitchStatus(Long roomId);
AiPlayer findByPlayerNo(List<AiPlayer> players, Integer no);
void usePoison(Long roomId);
boolean validateWitchTarget(List<AiPlayer> players, String poisonTarget, AiPlayer witch);
List<AiPlayer> findAliveAll(Long roomId);
void useAntidote(Long roomId);
List<AiPlayer> findAliveListByRole(Long roomId, String role);
long countAliveCivilian(List<AiPlayer> players);
long countAliveGod(List<AiPlayer> players);
boolean getWitchPoison(Long roomId);
boolean getWitchAntidote(Long roomId);
List<AiPlayer> findDeadLastNight(Long roomId);
}
package com.zhan.game.service;
import com.zhan.game.entity.AiPlayer;
import com.zhan.game.entity.Room;
import com.zhan.game.enums.GameResult;
import com.zhan.game.vo.GameStageAdvanceVO;
import java.util.List;
import java.util.Map;
public interface GameService {
/**
* 创建房间并生成12个AI玩家
*
* @param roomName 房间名称
* @return 房间信息
*/
Room createRoom(String roomName);
/**
* 清空夜间死亡记录(白天阶段结束后调用,避免跨夜混淆)
*
* @param roomId 房间ID
*/
void clearNightDeadRecord(Long roomId);
/**
* 记录夜间死亡玩家(狼人刀杀/女巫毒杀)
*
* @param roomId 房间ID
* @param killedByWolf 狼人刀杀目标(null表示被救/无刀人)
* @param poisonedByWitch 女巫毒杀目标(null表示未毒人)
* @param guardTarget 守卫守护目标(null表示未守护)
*/
void recordNightDeadPlayers(Long roomId, String killedByWolf, String poisonedByWitch, String guardTarget);
/**
* 执行完整夜晚流程:守卫 -> 狼人 -> 预言家 -> 女巫
*/
void nightStage(Long roomId);
/**
* 白天完整流程:
* 1. 公布昨夜死讯
* 2. 按顺序发言
* 3. 公投
* 4. 判胜负
*/
void dayStage(Long roomId);
/**
* 检查胜负结果
*
* @param roomId 房间ID
* @return 胜负结果
*/
GameResult checkWinOrLose(Long roomId);
/**
* 推进游戏阶段(核心接口:触发夜晚/白天流程)
* 调用逻辑:创建房间后,调用此接口推进游戏,自动完成当前阶段的所有AI行动
*
* @param roomId 房间ID
* @return 阶段推进结果(包含当前阶段、AI行动概要)
*/
GameStageAdvanceVO advanceGameStage(Long roomId);
/**
* 获取AI玩家的发言/行动记录(核心:查看AI对话内容)
*
* @param roomId 房间ID
* @param playerNo 玩家编号(可选,不传则返回所有AI的发言)
* @return 发言记录
*/
List<AiPlayer> getAiSpeakRecord(Long roomId, Integer playerNo);
/**
* 获取房间当前游戏状态(全局视角)
*
* @param roomId 房间ID
* @return 游戏状态(阶段、存活/死亡玩家、胜负结果等)
*/
Map<String, Object> getGameStatus(Long roomId);
/**
* 手动触发单个AI玩家发言(测试用,可选)
*
* @param roomId 房间ID
* @param playerNo 玩家编号
* @param prompt 自定义发言提示词(可选,不传则用默认提示词)
* @return 玩家发言内容
*/
Map<String, Object> triggerAiSpeak(Long roomId, Integer playerNo, String prompt);
/**
* AI投票统计
*
* @param list 投票玩家列表
* @param roomId 房间ID
* @return 最终投票目标
*/
String collectVoteFromAi(List<AiPlayer> list, Long roomId);
}
18.Service接口实现
package com.zhan.game.service.impl;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhan.game.entity.Room;
import com.zhan.game.mapper.RoomMapper;
import com.zhan.game.service.RoomService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class RoomServiceImpl extends ServiceImpl<RoomMapper, Room> implements RoomService {
@Override
public void updateRoomStatus(Long roomId, int status) {
super.update(new LambdaUpdateWrapper<Room>()
.set(Room::getStatus, status)
.eq(Room::getId, roomId)
);
}
@Override
public void updateRoomStage(Long roomId, String name) {
super.update(new LambdaUpdateWrapper<Room>()
.set(Room::getGameStage, name)
.eq(Room::getId, roomId)
);
}
}
package com.zhan.game.service.impl;
import com.zhan.game.service.RedisService;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* Redis通用工具类(统一处理对象/List/Hash存储)
*/
@Service
public class RedisServiceImpl implements RedisService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
// ========== 基础对象操作 ==========
@Override
public <T> void setObject(String key, T value) {
redisTemplate.opsForValue().set(key, value);
}
@Override
public <T> void setObject(String key, T value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getObject(String key, Class<T> clazz) {
Object value = redisTemplate.opsForValue().get(key);
return value != null ? (T) value : null;
}
// ========== List操作 ==========
@Override
public <T> void listRightPush(String key, T value) {
redisTemplate.opsForList().rightPush(key, value);
}
@Override
public <T> void listRightPushAll(String key, List<T> values) {
if (values != null && !values.isEmpty()) {
redisTemplate.opsForList().rightPushAll(key, values.toArray());
}
}
@SuppressWarnings("unchecked")
@Override
public <T> List<T> listRange(String key, long start, long end) {
return (List<T>) redisTemplate.opsForList().range(key, start, end);
}
// ========== Hash操作 ==========
@Override
public <T> void hashPut(String key, String hashKey, T value) {
redisTemplate.opsForHash().put(key, hashKey, value);
}
@Override
public <T> void hashPutAll(String key, Map<String, T> map) {
if (map != null && !map.isEmpty()) {
redisTemplate.opsForHash().putAll(key, map);
}
}
@SuppressWarnings("unchecked")
@Override
public <T> T hashGet(String key, String hashKey, Class<T> clazz) {
Object value = redisTemplate.opsForHash().get(key, hashKey);
return value != null ? (T) value : null;
}
// ========== 通用操作 ==========
@Override
public boolean hasKey(String key) {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
@Override
public void deleteKey(String key) {
redisTemplate.delete(key);
}
/**
* 获取Hash的所有键值对(解决entries读取的序列化问题)
*
* @param key Redis的Hash Key
* @return 键值对Map(自动处理序列化/反序列化)
*/
@Override
public Map<Object, Object> hashGetAllEntries(String key) {
if (!hasKey(key)) {
// 若key不存在,返回空Map而非null,避免业务代码空指针
return new HashMap<>();
}
return redisTemplate.opsForHash().entries(key);
}
@Override
public Boolean setIfAbsent(String key, Object value, long timeout, TimeUnit unit) {
return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
}
@Override
public Boolean deleteIfEquals(String key, Object value) {
return Objects.equals(redisTemplate.opsForValue().getAndDelete(key), value);
}
}
package com.zhan.game.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhan.game.config.ChatClientFactory;
import com.zhan.game.constants.GameConstants;
import com.zhan.game.entity.AiPlayer;
import com.zhan.game.enums.RoleEnum;
import com.zhan.game.enums.WitchStatusEnum;
import com.zhan.game.mapper.AiPlayerMapper;
import com.zhan.game.service.AiPlayerService;
import com.zhan.game.service.RedisService;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class AiPlayerServiceImpl extends ServiceImpl<AiPlayerMapper, AiPlayer> implements AiPlayerService {
@Resource
private ChatClientFactory chatClientFactory;
@Resource
private RedisService redisService;
/**
* 调用AI模型生成玩家发言/行动
*
* @param player AI玩家
* @param prompt 提示词(根据游戏阶段/角色生成)
* @return AI回复内容
*/
@Override
public String aiAction(AiPlayer player, String prompt) {
log.info("{}号玩家({}), 提示词:{}", player.getPlayerNo(), player.getRole(), prompt);
// 调用对应角色的AI模型
ChatClient chatClient = chatClientFactory.buildChatClient(player.getAiModelCode());
String reply = chatClient
.prompt(prompt)
.call()
.content();
log.info("{}号玩家({}), 回复内容:{}", player.getPlayerNo(), player.getRole(), reply);
// 更新玩家最后发言
player.setLastSpeak(reply);
this.updateById(player);
return reply;
}
@Override
public List<AiPlayer> findAliveAll(Long roomId) {
return super.list(new LambdaQueryWrapper<AiPlayer>()
.eq(AiPlayer::getRoomId, roomId)
.eq(AiPlayer::getStatus, GameConstants.ALIVE)
);
}
// 查找存活角色列表
@Override
public List<AiPlayer> findAliveListByRole(Long roomId, String role) {
return super.list(new LambdaQueryWrapper<AiPlayer>()
.eq(AiPlayer::getRoomId, roomId)
.eq(AiPlayer::getRole, role)
);
}
// 根据玩家编号查找
@Override
public AiPlayer findByPlayerNo(List<AiPlayer> players, Integer no) {
return players.stream()
.filter(p -> p.getPlayerNo().equals(no))
.findFirst()
.orElse(null);
}
// 统计存活神民
@Override
public long countAliveGod(List<AiPlayer> players) {
return players.stream()
.filter(p -> List.of("SEER", "WITCH", "HUNTER", "GUARD").contains(p.getRole()))
.filter(p -> GameConstants.ALIVE.equals(p.getStatus()))
.count();
}
// 统计存活平民
@Override
public long countAliveCivilian(List<AiPlayer> players) {
return players.stream()
.filter(p -> p.getRole().equals("CIVILIAN"))
.filter(p -> GameConstants.ALIVE.equals(p.getStatus()))
.count();
}
@Override
public List<AiPlayer> getRoomPlayers(Long roomId) {
return super.list(new LambdaQueryWrapper<AiPlayer>()
.eq(AiPlayer::getRoomId, roomId)
);
}
/**
* 初始化女巫药水状态(房间创建时调用)
*
* @param roomId 房间ID
*/
@Override
public void initWitchStatus(Long roomId) {
// Redis键格式:room:witch:状态:房间ID
redisService.setObject("room:witch:antidote:" + roomId, WitchStatusEnum.ANTIDOTE_AVAILABLE.getStatus(), 1, TimeUnit.HOURS);
redisService.setObject("room:witch:poison:" + roomId, WitchStatusEnum.POISON_AVAILABLE.getStatus(), 1, TimeUnit.HOURS);
}
/**
* 获取女巫解药状态
*
* @param roomId 房间ID
* @return true-可用 false-已用
*/
@Override
public boolean getWitchAntidote(Long roomId) {
Integer status = redisService.getObject("room:witch:antidote:" + roomId, Integer.class);
return status != null && status == WitchStatusEnum.ANTIDOTE_AVAILABLE.getStatus();
}
/**
* 获取女巫毒药状态
*
* @param roomId 房间ID
* @return true-可用 false-已用
*/
@Override
public boolean getWitchPoison(Long roomId) {
Integer status = redisService.getObject("room:witch:poison:" + roomId, Integer.class);
return status != null && status == WitchStatusEnum.POISON_AVAILABLE.getStatus();
}
/**
* 使用解药(更新状态)
*
* @param roomId 房间ID
*/
@Override
public void useAntidote(Long roomId) {
redisService.setObject("room:witch:antidote:" + roomId, WitchStatusEnum.ANTIDOTE_USED.getStatus(), 1, TimeUnit.HOURS);
}
/**
* 使用毒药(更新状态)
*
* @param roomId 房间ID
*/
@Override
public void usePoison(Long roomId) {
redisService.setObject("room:witch:poison:" + roomId, WitchStatusEnum.POISON_USED.getStatus(), 1, TimeUnit.HOURS);
}
/**
* 校验女巫目标合法性(不能毒自己、不能毒已死玩家)
*
* @param players 所有玩家
* @param poisonTarget 毒药目标编号
* @param witch 女巫本人
* @return true-合法 false-不合法
*/
@Override
public boolean validateWitchTarget(List<AiPlayer> players, String poisonTarget, AiPlayer witch) {
// 1. 校验参数有效性
if (players == null || poisonTarget == null || witch == null) {
return false;
}
// 2. 校验是否为数字且在合理范围内
if (!poisonTarget.matches("\\d+")) {
return false;
}
int targetNo;
try {
targetNo = Integer.parseInt(poisonTarget);
} catch (NumberFormatException e) {
return false;
}
// 3. 校验目标编号是否在合理范围内
if (targetNo <= 0) { // 玩家编号通常从1开始
return false;
}
// 4. 校验女巫编号是否有效
Integer witchNo = witch.getPlayerNo();
if (witchNo == null) {
return false;
}
// 5. 不能毒自己
if (targetNo == witchNo) {
return false;
}
// 6. 不能毒已死亡玩家
AiPlayer targetPlayer = findByPlayerNo(players, targetNo);
return targetPlayer != null && GameConstants.ALIVE.equals(targetPlayer.getStatus());
}
/**
* 核心方法:查找上一夜死亡的玩家
*
* @param roomId 房间ID
* @return 上一夜死亡的AI玩家列表(空列表表示平安夜)
*/
@Override
public List<AiPlayer> findDeadLastNight(Long roomId) {
// 验证roomId参数
if (roomId == null || roomId <= 0) {
return new ArrayList<>();
}
try {
// 1. 从Redis获取上一夜死亡的玩家编号
String json = redisService.getObject("room:night:dead:" + roomId, String.class);
if (StringUtils.isBlank(json)) {
return new ArrayList<>();
}
// 将死玩家编号转换为Set以提高查找效率
List<Integer> deadNos = JSON.parseArray(json, Integer.class);
Set<Integer> deadNoSet = new HashSet<>(deadNos);
// 2. 获取房间所有玩家
List<AiPlayer> allPlayers = getRoomPlayers(roomId);
if (allPlayers == null || allPlayers.isEmpty()) {
return new ArrayList<>();
}
// 3. 筛选出死亡玩家(编号匹配 + 状态为DEAD)
return allPlayers.stream()
.filter(player -> player != null
&& player.getPlayerNo() != null
&& deadNoSet.contains(player.getPlayerNo())
&& GameConstants.DEAD.equals(player.getStatus()))
.collect(Collectors.toList());
} catch (Exception e) {
// 记录异常日志,返回空列表作为安全默认值
log.error("查询房间 {} 上一夜死亡玩家时发生异常", roomId, e);
return new ArrayList<>();
}
}
}
package com.zhan.game.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zhan.game.common.ResultCode;
import com.zhan.game.constants.GameConstants;
import com.zhan.game.entity.AiPlayer;
import com.zhan.game.entity.Room;
import com.zhan.game.enums.GameResult;
import com.zhan.game.enums.GameStageEnum;
import com.zhan.game.enums.ModelEnum;
import com.zhan.game.enums.RoleEnum;
import com.zhan.game.exception.BizException;
import com.zhan.game.service.AiPlayerService;
import com.zhan.game.service.GameService;
import com.zhan.game.service.RedisService;
import com.zhan.game.service.RoomService;
import com.zhan.game.vo.GameStageAdvancePlayerVO;
import com.zhan.game.vo.GameStageAdvanceVO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.zhan.game.enums.RoleEnum.WEREWOLF;
import static java.util.Arrays.stream;
@Slf4j
@Service
public class GameServiceImpl implements GameService {
@Resource
private RoomService roomService;
@Resource
private AiPlayerService aiPlayerService;
@Resource
private RedisService redisService;
/**
* 创建房间并生成12个AI玩家
*
* @param roomName 房间名称
* @return 房间信息
*/
@Transactional(rollbackFor = Exception.class)
@Override
public Room createRoom(String roomName) {
String lockKey = "room:create:lock:" + roomName;
String lockValue = UUID.randomUUID().toString();
try {
// 使用分布式锁防止并发创建同名房间
Boolean lockAcquired = redisService.setIfAbsent(lockKey, lockValue, 30, TimeUnit.SECONDS);
if (!lockAcquired) {
throw new BizException(ResultCode.ROOM_CREATION_BUSY);
}
// 检查房间是否已存在
Room existingRoom = roomService.getOne(new LambdaQueryWrapper<Room>()
.eq(Room::getRoomName, roomName)
.last("limit 1"));
if (Objects.nonNull(existingRoom)) {
throw new BizException(ResultCode.ROOM_EXIST);
}
// 1. 创建房间
Room room = new Room();
room.setRoomName(roomName);
room.setPlayerCount(GameConstants.DEFAULT_PLAYER_COUNT);
room.setGameStage(GameStageEnum.NIGHT.getName());
room.setStatus(GameConstants.ROOM_STATUS_ACTIVE); // 1-进行中
room.setCreateTime(LocalDateTime.now());
room.setUpdateTime(LocalDateTime.now());
roomService.save(room);
// 生成AI玩家并分配身份
// 2. 生成角色列表(随机打乱)
List<RoleEnum> roles = RoleEnum.getFullRoles();
// 3. 生成AI玩家,分配编号,绑定角色和AI模型
List<AiPlayer> aiPlayers = new ArrayList<>();
for (int i = 0; i < roles.size(); i++) {
RoleEnum roleEnum = roles.get(i);
AiPlayer player = new AiPlayer();
player.setRoomId(room.getId());
player.setPlayerNo(i + GameConstants.PLAYER_NO_START_INDEX); // 玩家编号1-12
player.setRole(roleEnum.getName());
player.setAiModelCode(ModelEnum.getModeCodeByName(roleEnum.getName()).getCode());
player.setStatus(GameConstants.ALIVE); // 初始存活
aiPlayers.add(player);
}
// 4. 保存AI玩家到数据库
aiPlayerService.saveBatch(aiPlayers);
// 5. 缓存房间和玩家信息到Redis(方便快速查询)
try {
redisService.setObject("room:" + room.getId(), room, 1, TimeUnit.HOURS);
redisService.listRightPushAll("room:players:" + room.getId(), aiPlayers);
} catch (Exception e) {
log.warn("Redis缓存设置失败,不影响主要业务流程", e);
}
// 6. 初始化女巫药水状态
aiPlayerService.initWitchStatus(room.getId());
// 初始化全局游戏信息
initGameGlobalInfo(room.getId());
return room;
} finally {
// 释放分布式锁
try {
redisService.deleteIfEquals(lockKey, lockValue);
} catch (Exception e) {
log.warn("释放分布式锁失败", e);
}
}
}
/**
* 清空夜间死亡记录(白天阶段结束后调用,避免跨夜混淆)
*
* @param roomId 房间ID
*/
@Override
public void clearNightDeadRecord(Long roomId) {
redisService.deleteKey("room:night:dead:" + roomId);
}
/**
* 记录夜间死亡玩家(狼人刀杀/女巫毒杀)
*
* @param roomId 房间ID
* @param killedByWolf 狼人刀杀目标(null表示被救/无刀人)
* @param poisonedByWitch 女巫毒杀目标(null表示未毒人)
* @param guardTarget 守卫守护目标(null表示未守护)
*/
@Override
public void recordNightDeadPlayers(Long roomId, String killedByWolf, String poisonedByWitch, String guardTarget) {
List<Integer> deadNos = new ArrayList<>();
// 1. 处理狼人刀杀:未被守卫守护 + 未被解药拯救
if (StringUtils.isNotBlank(killedByWolf) && !Objects.equals(killedByWolf, guardTarget)) {
deadNos.add(Integer.parseInt(killedByWolf));
}
// 2. 处理女巫毒杀:有效目标
if (StringUtils.isNotBlank(poisonedByWitch) && !Objects.equals("0", poisonedByWitch)) {
deadNos.add(Integer.parseInt(poisonedByWitch));
}
// 3. 存入Redis:键格式 room:night:dead:房间ID,值为死亡玩家编号列表
redisService.setObject("room:night:dead:" + roomId, JSON.toJSONString(deadNos), 1, TimeUnit.HOURS);
log.info("房间{}:夜间死亡玩家编号列表 -> {}", roomId, deadNos);
}
/**
* 执行完整夜晚流程:守卫 -> 狼人 -> 预言家 -> 女巫
*/
@Override
public void nightStage(Long roomId) {
log.info("房间{}:开始执行完整夜晚流程", roomId);
List<AiPlayer> players = aiPlayerService.getRoomPlayers(roomId);
// ====================== 1. 守卫守护 ======================
AiPlayer guard = players.stream()
.filter(p -> p.getRole().equals(RoleEnum.GUARD.getName()) && GameConstants.ALIVE.equals(p.getStatus()))
.findFirst()
.orElse(null);
String guardTarget = null;
if (Objects.nonNull(guard)) {
String prompt = """
你是狼人杀守卫,根据历史游戏信息和自己的判断,今晚请选择一名玩家守护(不能连续两晚守同一个人)。
只回复数字,不要多余内容。
""";
guardTarget = aiPlayerService.aiAction(guard, prompt);
redisService.setObject("room:guard:last:" + roomId, guardTarget, 1, TimeUnit.HOURS);
// 记录守卫行动
recordNightAction(roomId, "guardProtect", guardTarget);
}
// ====================== 2. 狼人刀人 ======================
List<AiPlayer> werewolves = aiPlayerService.findAliveListByRole(roomId, WEREWOLF.getName());
String killTarget = null;
if (CollectionUtils.isNotEmpty(werewolves)) {
killTarget = collectVoteFromAi(werewolves, roomId);
// 记录狼人刀人行动
recordNightAction(roomId, "wolfKill", killTarget);
}
// ====================== 3. 预言家查验 ======================
AiPlayer seer = players.stream()
.filter(p -> p.getRole().equals(RoleEnum.SEER.getName()) && GameConstants.ALIVE.equals(p.getStatus()))
.findFirst()
.orElse(null);
String checkResult = null;
if (Objects.nonNull(seer)) {
String prompt = String.format("""
你是%s号预言家,晚上选择一名玩家查验身份。
规则:
1.只能回复1-12之间的数字(排除自己%s),不要任何多余内容(如文字、符号、空格)。
2.根据你的判断,优先查验你认为可能是狼人的其他玩家。
根据历史游戏信息和自己的判断,请直接回复目标玩家编号:
""", seer.getPlayerNo(), seer.getPlayerNo());
String checkTarget = aiPlayerService.aiAction(seer, prompt);
AiPlayer target = aiPlayerService.findByPlayerNo(players, Integer.parseInt(checkTarget));
checkResult = target.getRole().equals(RoleEnum.WEREWOLF.getName()) ? "狼人" : "好人";
redisService.setObject("room:seer:result:" + roomId + ":" + seer.getPlayerNo(), checkResult, 1, TimeUnit.HOURS);
// 记录预言家查验行动
recordNightAction(roomId, "seerCheck", checkTarget);
}
// ====================== 4. 女巫用药 ======================
AiPlayer witch = players.stream()
.filter(p -> p.getRole().equals(RoleEnum.WITCH.getName()) && GameConstants.ALIVE.equals(p.getStatus()))
.findFirst()
.orElse(null);
aiPlayerService.initWitchStatus(roomId);
boolean hasPoison = aiPlayerService.getWitchPoison(roomId);
boolean hasAntidote = aiPlayerService.getWitchAntidote(roomId);
boolean usedAntidote = false;
String poisonTarget = null;
String witchUse = "未用药";
if (Objects.nonNull(witch)) {
// 解药逻辑
if (hasAntidote && StringUtils.isNotBlank(killTarget)) {
String savePrompt = String.format("""
你是%d号女巫,当前解药可用、毒药%s。
今夜%d号玩家被狼人刀杀,你是否使用解药拯救他?
注意:解药整局只能用1次,且不能和毒药同晚使用。
根据历史游戏信息和自己的判断,仅回复:是 或 否,不要多余内容。
""", witch.getPlayerNo(), hasPoison ? "可用" : "已用", Integer.parseInt(killTarget));
String saveAns = aiPlayerService.aiAction(witch, savePrompt).trim();
if (Objects.equals(saveAns, "是")) {
aiPlayerService.useAntidote(roomId);
log.info("女巫{}号使用解药拯救了{}号玩家", witch.getPlayerNo(), killTarget);
witchUse = "使用解药拯救" + killTarget + "号玩家";
usedAntidote = true;
killTarget = null;
}
}
// 毒药逻辑
if (hasPoison && !usedAntidote) {
List<AiPlayer> aliveAll = aiPlayerService.findAliveAll(roomId);
List<String> aliveNos = aliveAll.stream()
.map(p -> String.valueOf(p.getPlayerNo()))
.toList();
String poisonPrompt = String.format("""
你是%d号女巫,当前毒药可用、解药%s。
你可以选择毒杀一名玩家(整局仅1次机会),也可以选择不毒。
存活玩家编号:%s
根据历史游戏信息和自己的判断,给出你的回复。
规则:不能毒自己、不能毒已死玩家、不能和解药同晚使用。
不毒回复:0;毒杀回复对应玩家编号,仅回复数字,不要多余内容。
""", witch.getPlayerNo(), hasAntidote ? "可用" : "已用", String.join("、", aliveNos));
poisonTarget = aiPlayerService.aiAction(witch, poisonPrompt).trim();
if (!Objects.equals("0", poisonTarget) && aiPlayerService.validateWitchTarget(players, poisonTarget, witch)) {
aiPlayerService.usePoison(roomId);
AiPlayer poisoned = aiPlayerService.findByPlayerNo(players, Integer.parseInt(poisonTarget));
poisoned.setStatus(GameConstants.DEAD);
aiPlayerService.updateById(poisoned);
witchUse = "使用毒药毒杀" + poisonTarget + "号玩家";
log.info("女巫{}号使用毒药毒杀了{}号玩家", witch.getPlayerNo(), poisonTarget);
}
}
// 记录女巫用药行动
recordNightAction(roomId, "witchUse", witchUse);
}
// ====================== 最终死亡判定 ======================
List<String> deadNos = new ArrayList<>();
if (Objects.nonNull(killTarget) && !Objects.equals(killTarget, guardTarget)) {
AiPlayer killed = aiPlayerService.findByPlayerNo(players, Integer.valueOf(killTarget));
killed.setStatus(GameConstants.DEAD);
aiPlayerService.updateById(killed);
deadNos.add(killTarget);
}
if (Objects.nonNull(poisonTarget) && !Objects.equals("0", poisonTarget) && aiPlayerService.validateWitchTarget(players, poisonTarget, witch)) {
deadNos.add(poisonTarget);
}
// 记录夜间死亡玩家
// redisService.listRightPushAll("room:global:dead:" + roomId,deadNos);
// 记录夜间死亡玩家(狼人刀杀/女巫毒杀)
recordNightDeadPlayers(roomId, killTarget, poisonTarget, guardTarget);
// 阶段切换
roomService.updateRoomStage(roomId, GameStageEnum.DAY.getName());
}
/**
* 白天完整流程:
* 1. 公布昨夜死讯
* 2. 按顺序发言
* 3. 公投
* 4. 判胜负
*/
@Override
public void dayStage(Long roomId) {
List<AiPlayer> players = aiPlayerService.getRoomPlayers(roomId);
List<AiPlayer> deadLastNight = aiPlayerService.findDeadLastNight(roomId);
// ====================== 1. 公布死讯 ======================
if (CollectionUtils.isNotEmpty(deadLastNight)) {
String deadNos = deadLastNight.stream().map(p -> p.getPlayerNo().toString()).reduce((a, b) -> a + "、" + b).orElse("");
log.info("昨夜死亡玩家:{}", deadNos);
} else {
log.info("昨夜平安夜");
}
// ====================== 2. 存活玩家依次发言 ======================
List<AiPlayer> alive = aiPlayerService.findAliveAll(roomId);
for (AiPlayer p : alive) {
// 拼接完整提示词:全局信息 + 历史发言 + 角色专属信息 + 发言要求
String nightActionInfo = getNightActionInfo(roomId, p);
String historySpeak = getHistorySpeakText(roomId);
String roleName = p.getRole();
String prompt = String.format("""
【狼人杀游戏信息】
你的身份:%d号%s
场上局势:
%s
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
%s
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
""", p.getPlayerNo(), roleName, nightActionInfo, historySpeak);
// 调用AI发言
String speakContent = aiPlayerService.aiAction(p, prompt).trim();
// 记录当前玩家发言(供后续玩家查看)
recordPlayerSpeak(roomId, p.getPlayerNo(), speakContent);
// 更新玩家最后发言
p.setLastSpeak(speakContent);
aiPlayerService.updateById(p);
log.info("{}号玩家({})发言:{}", p.getPlayerNo(), roleName, speakContent);
}
// ====================== 3. 公投放逐 ======================
String voteOutTarget = collectVoteFromAi(alive, roomId);
if (StringUtils.isNotBlank(voteOutTarget)) {
AiPlayer voted = aiPlayerService.findByPlayerNo(players, Integer.valueOf(voteOutTarget));
voted.setStatus(GameConstants.DEAD);
aiPlayerService.updateById(voted);
log.info("公投结果:{}号玩家被放逐,身份{}", voteOutTarget, voted.getRole());
// 猎人被放逐触发开枪
if (Objects.equals(voted.getRole(), RoleEnum.HUNTER.getName())) {
hunterShoot(roomId, voted);
}
}
// ====================== 4. 屠边判胜负 ======================
GameResult result = checkWinOrLose(roomId);
if (result != GameResult.CONTINUE) {
roomService.updateRoomStatus(roomId, 2);
roomService.updateRoomStage(roomId, GameStageEnum.END.getName());
log.info("游戏结束,胜利者:{}", result);
} else {
roomService.updateRoomStage(roomId, GameStageEnum.NIGHT.getName());
}
clearNightDeadRecord(roomId);
clearSpeakRecord(roomId);
}
/**
* 检查胜负结果
*
* @param roomId 房间ID
* @return 胜负结果
*/
@Override
public GameResult checkWinOrLose(Long roomId) {
List<AiPlayer> players = aiPlayerService.getRoomPlayers(roomId);
// 存活数量统计
long wolfAlive = aiPlayerService.findAliveListByRole(roomId, RoleEnum.WEREWOLF.getName()).size();
long godAlive = players.stream()
.filter(p -> List.of(RoleEnum.SEER.getName(), RoleEnum.WITCH.getName(), RoleEnum.HUNTER.getName(), RoleEnum.GUARD.getName()).contains(p.getRole()))
.filter(p -> GameConstants.ALIVE.equals(p.getStatus()))
.count();
long civilianAlive = aiPlayerService.findAliveListByRole(roomId, RoleEnum.CIVILIAN.getName()).size();
// 狼人胜利条件:屠神(神全死)或 屠民(民全死),且至少1狼存活
if (wolfAlive > 0 && (godAlive == 0 || civilianAlive == 0)) {
log.info("狼人胜利:屠神(神全死)或 屠民(民全死),且至少1狼存活");
return GameResult.WOLF_WIN;
}
// 好人胜利条件:狼全死
if (wolfAlive == 0) {
log.info("好人胜利:狼全死");
return GameResult.GOOD_WIN;
}
// 游戏继续
return GameResult.CONTINUE;
}
/**
* 推进游戏阶段(核心接口:触发夜晚/白天流程)
* 调用逻辑:创建房间后,调用此接口推进游戏,自动完成当前阶段的所有AI行动
*
* @param roomId 房间ID
* @return 阶段推进结果(包含当前阶段、AI行动概要)
*/
@Override
public GameStageAdvanceVO advanceGameStage(Long roomId) {
log.info("开始推进游戏阶段");
GameStageAdvanceVO result = new GameStageAdvanceVO();
try {
// 1. 获取当前房间信息
Room room = roomService.getById(roomId);
if (Objects.isNull(room)) {
throw new BizException(ResultCode.ROOM_NOT_FOUND);
}
if (Objects.equals(GameStageEnum.END.getName(), room.getGameStage())) {
throw new BizException(ResultCode.GAME_OVER);
}
// 2. 根据当前阶段推进流程
String currentStage = room.getGameStage();
if (Objects.equals(GameStageEnum.NIGHT.getName(), currentStage)) {
// 执行夜晚流程:守卫→狼人→预言家→女巫→死亡判定
nightStage(roomId);
result.setStage(GameStageEnum.DAY.getName());
result.setStageMsg("夜晚阶段已完成,进入白天阶段");
// 返回夜间死亡玩家
List<AiPlayer> deadLastNight = aiPlayerService.findDeadLastNight(roomId);
result.setDeadLastNight(deadLastNight.stream()
.map(p -> new GameStageAdvancePlayerVO(p.getPlayerNo(), p.getRole()))
.toList());
} else if (Objects.equals(GameStageEnum.DAY.getName(), currentStage)) {
// 执行白天流程:公布死讯→发言→公投→判胜负
dayStage(roomId);
Room updatedRoom = roomService.getById(roomId);
result.setStage(updatedRoom.getGameStage());
if (Objects.equals(GameStageEnum.END.getName(), updatedRoom.getGameStage())) {
GameResult gameResult = checkWinOrLose(roomId);
result.setStageMsg("游戏结束");
result.setGameResult(gameResult);
} else {
result.setStageMsg("白天阶段已完成,进入夜晚阶段");
}
// 返回白天公投死亡玩家
List<AiPlayer> allPlayers = aiPlayerService.getRoomPlayers(roomId);
List<AiPlayer> deadLastNight = aiPlayerService.findDeadLastNight(roomId);
List<AiPlayer> votedDead = allPlayers.stream()
.filter(player -> GameConstants.DEAD.equals(player.getStatus()) && !deadLastNight.contains(player))
.toList();
result.setVotedDead(votedDead.stream()
.map(p -> new GameStageAdvancePlayerVO(p.getPlayerNo(), p.getRole()))
.toList());
}
result.setRoomId(roomId);
return result;
} catch (Exception e) {
log.error("推进游戏阶段失败", e);
throw new BizException(ResultCode.ADVANCE_GAME_STAGE_ERROR);
}
}
/**
* 获取AI玩家的发言/行动记录(核心:查看AI对话内容)
*
* @param roomId 房间ID
* @param playerNo 玩家编号(可选,不传则返回所有AI的发言)
* @return 发言记录
*/
@Override
public List<AiPlayer> getAiSpeakRecord(Long roomId, Integer playerNo) {
try {
List<AiPlayer> players = aiPlayerService.getRoomPlayers(roomId);
if (CollectionUtils.isEmpty(players)) {
throw new BizException(ResultCode.ROOM_NO_PLAYER);
}
// 1. 查询指定玩家的发言
if (Objects.nonNull(playerNo)) {
AiPlayer targetPlayer = players.stream()
.filter(p -> p.getPlayerNo().equals(playerNo))
.findFirst()
.orElse(null);
if (Objects.isNull(targetPlayer)) {
throw new BizException(ResultCode.PLAYER_NOT_FOUND);
}
return List.of(targetPlayer);
} else {
// 2. 查询所有玩家的发言
return players;
}
} catch (Exception e) {
log.error("获取AI发言记录失败", e);
throw new BizException(ResultCode.GET_AI_SPEAK_RECORD_ERROR);
}
}
/**
* 获取房间当前游戏状态(全局视角)
*
* @param roomId 房间ID
* @return 游戏状态(阶段、存活/死亡玩家、胜负结果等)
*/
@Override
public Map<String, Object> getGameStatus(Long roomId) {
Map<String, Object> result = new HashMap<>();
try {
Room room = roomService.getById(roomId);
if (Objects.isNull(room)) {
throw new BizException(ResultCode.ROOM_NOT_FOUND);
}
List<AiPlayer> allPlayers = aiPlayerService.getRoomPlayers(roomId);
// 存活/死亡玩家分类
List<AiPlayer> alivePlayers = allPlayers.stream().filter(p -> GameConstants.ALIVE.equals(p.getStatus())).toList();
List<AiPlayer> deadPlayers = allPlayers.stream().filter(p -> GameConstants.DEAD.equals(p.getStatus())).toList();
// 组装返回结果
result.put("code", 200);
result.put("roomInfo", Map.of(
"roomId", room.getId(),
"roomName", room.getRoomName(),
"gameStage", room.getGameStage(),
"gameStageName", GameStageEnum.valueOf(room.getGameStage()).getName(),
"status", room.getStatus()));
result.put("alivePlayers", alivePlayers.stream()
.map(p -> Map.of(
"playerNo", p.getPlayerNo(),
"role", p.getRole()))
.toList());
result.put("deadPlayers", deadPlayers.stream()
.map(p -> Map.of(
"playerNo", p.getPlayerNo(),
"role", p.getRole()))
.toList());
// 游戏结束则返回胜负结果
if (Objects.equals(GameStageEnum.END.getName(), room.getGameStage())) {
result.put("gameResult", checkWinOrLose(roomId));
}
return result;
} catch (Exception e) {
log.error("获取游戏状态失败", e);
throw new BizException(ResultCode.GET_GAME_STATUS_ERROR);
}
}
/**
* 手动触发单个AI玩家发言(测试用,可选)
*
* @param roomId 房间ID
* @param playerNo 玩家编号
* @param prompt 自定义发言提示词(可选,不传则用默认提示词)
* @return 玩家发言内容
*/
@Override
public Map<String, Object> triggerAiSpeak(Long roomId, Integer playerNo, String prompt) {
Map<String, Object> result = new HashMap<>();
try {
List<AiPlayer> players = aiPlayerService.getRoomPlayers(roomId);
AiPlayer targetPlayer = players.stream()
.filter(p -> p.getPlayerNo().equals(playerNo) && GameConstants.ALIVE.equals(p.getStatus()))
.findFirst()
.orElse(null);
if (Objects.isNull(targetPlayer)) {
throw new BizException(ResultCode.PLAYER_DEAD_OR_NOT_FOUND);
}
// 使用自定义提示词或默认提示词
String format = String.format("你是%d号%s,现在是游戏发言阶段,请根据局势发表你的观点,50-100字。", targetPlayer.getPlayerNo(), RoleEnum.valueOf(targetPlayer.getRole()).getName());
String finalPrompt = StringUtils.isNotBlank(prompt) ? prompt : format;
// 调用AI生成发言
String speakContent = aiPlayerService.aiAction(targetPlayer, finalPrompt);
result.put("code", 200);
result.put("playerNo", playerNo);
result.put("role", targetPlayer.getRole());
result.put("speakContent", speakContent);
return result;
} catch (Exception e) {
log.error("触发AI发言失败", e);
throw new BizException(ResultCode.TRIGGER_AI_SPEAK_ERROR);
}
}
/**
* 猎人被放逐/刀杀触发开枪
*/
private void hunterShoot(Long roomId, AiPlayer hunter) {
List<AiPlayer> alivePlayers = aiPlayerService.findAliveAll(roomId);
String prompt = String.format("""
你是%d号猎人,被放逐/刀杀,可开枪带走一名玩家。
存活玩家:%s
请选择带走的玩家编号,只回复数字。
""", hunter.getPlayerNo(), alivePlayers.stream().map(p -> p.getPlayerNo().toString()).collect(Collectors.joining("、")));
String shootTarget = aiPlayerService.aiAction(hunter, prompt).trim();
if (shootTarget.matches("\\d+")) {
AiPlayer target = aiPlayerService.findByPlayerNo(alivePlayers, Integer.valueOf(shootTarget));
if (Objects.nonNull(target)) {
target.setStatus(GameConstants.DEAD);
aiPlayerService.updateById(target);
log.info("猎人{}号开枪带走了{}号玩家", hunter.getPlayerNo(), shootTarget);
}
}
}
/**
* 初始化游戏全局信息(房间创建时调用)
* 修复:移除空集合的rightPushAll,避免非空校验报错
*/
public void initGameGlobalInfo(Long roomId) {
// 1. 夜间行动记录:初始化空Hash(用String类型占位)
redisService.hashPut("room:global:night:action:" + roomId, "init", "ok");
// 2. 狼人队友列表:仅当有狼人时才存储(确保存入String类型)
List<AiPlayer> wolves = aiPlayerService.findAliveListByRole(roomId, RoleEnum.WEREWOLF.getName());
if (CollectionUtils.isNotEmpty(wolves)) {
List<String> wolfNos = wolves.stream().map(p -> String.valueOf(p.getPlayerNo())) // 明确转为String
.filter(str -> !str.isEmpty()) // 过滤空值
.toList();
if (CollectionUtils.isNotEmpty(wolfNos)) { // 二次校验,避免空列表
redisService.listRightPushAll("room:global:wolf:teammate:" + roomId, wolfNos);
}
}
}
/**
* 记录玩家发言(用于后续玩家查看)
*/
public void recordPlayerSpeak(Long roomId, Integer playerNo, String speakContent) {
if (playerNo == null || speakContent == null || speakContent.trim().isEmpty()) {
log.warn("发言记录失败:玩家编号/内容为空,roomId={}, playerNo={}", roomId, playerNo);
return;
}
String speakKey = "room:global:speak:" + roomId;
String speakRecord = playerNo + ":" + speakContent.trim();
// 首次调用时,Redis会自动创建空列表并添加元素(无需提前初始化)
redisService.listRightPush(speakKey, speakRecord);
}
/**
* 获取历史发言记录(拼接成文本)
*/
public String getHistorySpeakText(Long roomId) {
String speakKey = "room:global:speak:" + roomId;
// 先判断key是否存在,避免返回null
if (!redisService.hasKey(speakKey)) {
return "暂无玩家发言";
}
// 获取全部发言记录(0到-1表示全量)
List<Object> speakList = redisService.listRange(speakKey, 0, -1);
if (CollectionUtils.isEmpty(speakList)) {
return "暂无玩家发言";
}
List<String> list = speakList.stream().map(Object::toString).toList();
StringBuilder sb = new StringBuilder();
for (String speak : list) {
String[] split = speak.split(":", 2); // 按第一个":"分割(避免发言含":")
if (split.length == 2) { // 校验格式,避免数组越界
sb.append(split[0]).append("号:").append(split[1]).append("\n");
}
}
return sb.toString().trim(); // 去除末尾换行
}
/**
* 记录夜间行动(如狼人刀人、预言家查验、女巫用药、守卫守护)
*/
public void recordNightAction(Long roomId, String actionType, String value) {
redisService.hashPut("room:global:night:action:" + roomId, actionType, value);
}
/**
* 获取夜间行动信息
*/
public String getNightActionInfo(Long roomId, AiPlayer player) {
Map<Object, Object> actionMap = redisService.hashGetAllEntries("room:global:night:action:" + roomId);
StringBuilder sb = new StringBuilder();
// 通用信息:昨夜死亡玩家
String json = redisService.getObject("room:night:dead:" + roomId, String.class);
if (StringUtils.isNotBlank(json)) {
List<String> deadLastNight = JSON.parseArray(json, String.class);
List<String> list = deadLastNight.stream().map(Object::toString).toList();
sb.append("昨夜死亡玩家:").append(String.join("、", list)).append("\n");
} else {
sb.append("昨夜平安夜\n");
}
// 角色专属信息
String role = player.getRole();
RoleEnum roleEnum = RoleEnum.getRoleByName(role);
if (Objects.isNull(roleEnum)) {
throw new BizException(ResultCode.ROLE_NOT_FOUND);
}
switch (roleEnum) {
// 原狼人专属信息部分修改
case WEREWOLF:
// 狼人:知道队友+昨夜刀人目标(补充非空校验)
String wolfTeammateKey = "room:global:wolf:teammate:" + roomId;
List<String> wolfTeammates = new ArrayList<>();
if (Boolean.TRUE.equals(redisService.hasKey(wolfTeammateKey))) {
// 关键修复:强制转换为String类型(避免Object解析失败)
List<Object> tempList = redisService.listRange(wolfTeammateKey, 0, -1);
if (tempList != null && !tempList.isEmpty()) {
wolfTeammates = tempList.stream().map(obj -> obj != null ? obj.toString() : "") // 转为字符串
.filter(str -> !str.isEmpty()) // 过滤空值
.toList();
}
}
sb.append("你的狼人队友:").append(wolfTeammates.isEmpty() ? "无" : String.join("、", wolfTeammates)).append("\n");
sb.append("昨夜狼人刀人目标:").append(actionMap.getOrDefault("wolfKill", "无")).append("\n");
break;
case SEER:
// 预言家:知道自己的查验结果
String checkResult = redisService.getObject("room:seer:result:" + roomId + ":" + player.getPlayerNo(), String.class);
String checkTarget = (String) actionMap.getOrDefault("seerCheck", "无");
sb.append("你昨夜查验").append(checkTarget).append("号玩家,身份是:").append(checkResult).append("\n");
break;
case WITCH:
// 女巫:知道药水状态+昨夜被刀玩家+自己用药情况
boolean hasAntidote = aiPlayerService.getWitchAntidote(roomId);
boolean hasPoison = aiPlayerService.getWitchPoison(roomId);
sb.append("你的解药状态:").append(hasAntidote ? "可用" : "已用").append("\n");
sb.append("你的毒药状态:").append(hasPoison ? "可用" : "已用").append("\n");
sb.append("昨夜被刀玩家:").append(actionMap.getOrDefault("wolfKill", "无")).append("\n");
sb.append("你昨夜用药情况:").append(actionMap.getOrDefault("witchUse", "未用药")).append("\n");
break;
case GUARD:
// 守卫:知道自己昨夜守护目标
sb.append("你昨夜守护的玩家:").append(actionMap.getOrDefault("guardProtect", "无")).append("\n");
break;
default:
// 平民/猎人:只有通用信息
break;
}
return sb.toString();
}
/**
* 清空本轮发言记录(白天阶段结束后调用)
*/
public void clearSpeakRecord(Long roomId) {
redisService.deleteKey("room:global:speak:" + roomId);
}
/**
* 生成放逐投票提示词(按角色定制,禁止自投)
*
* @param playerNo 玩家编号
* @param role 玩家身份
* @param suspectNos 怀疑的狼人编号列表
* @return 精准提示词
*/
public String generateVotePrompt(String playerNo, String role, List<String> suspectNos) {
// 好人(预言家/女巫/守卫/平民)提示词
if (!WEREWOLF.getName().equals(role)) {
return String.format("""
你是%d号玩家,身份是%s(好人),正在参与放逐投票。
规则:
1. 绝对不能投票给自己(%d号);
2. 优先投票你怀疑的狼人(怀疑列表:%s);
3. 只回复1-12之间的数字,不要加任何文字、符号、括号;
4. 你的目标是投出狼人,不要投好人。
请直接回复你要投票的玩家编号:
""", Integer.parseInt(playerNo), role, Integer.parseInt(playerNo),
suspectNos.isEmpty() ? "无,优先投发言异常的玩家" : String.join("、", suspectNos));
}
// 狼人提示词(也禁止自投,优先投好人)
else {
return String.format("""
你是%d号玩家,身份是狼人,正在参与放逐投票。
规则:
1. 绝对不能投票给自己(%d号);
2. 优先投票你识别的好人/神牌;
3. 只回复1-12之间的数字,不要加任何文字、符号、括号;
4. 你的目标是投出好人,保护狼人队友。
请直接回复你要投票的玩家编号:
""", Integer.parseInt(playerNo), Integer.parseInt(playerNo));
}
}
/**
* AI投票统计
*
* @param list 投票玩家列表
* @param roomId 房间ID
* @return 最终投票目标
*/
@Override
public String collectVoteFromAi(List<AiPlayer> list, Long roomId) {
Map<String, Integer> cnt = new HashMap<>();
List<AiPlayer> alivePlayers = aiPlayerService.findAliveAll(roomId);
List<String> aliveNos = alivePlayers.stream().map(p -> p.getPlayerNo().toString()).toList();
for (AiPlayer p : list) {
if (GameConstants.ALIVE.equals(p.getStatus())) {
String prompt = "";
if (Objects.equals(p.getRole(), RoleEnum.WEREWOLF.getName())) {
prompt = String.format("""
【狼人晚上集体刀人】
你是%d号玩家,身份是狼人,正在参与放逐投票。
历史发言:
%s
场上存活玩家:%s
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
""", p.getPlayerNo(), getHistorySpeakText(roomId), alivePlayers.stream().map(item -> item.getPlayerNo().toString()).collect(Collectors.joining("、")));
} else {
prompt = String.format("""
【狼人杀公投】
你是%d号玩家,身份是%s(好人),正在参与放逐投票。
历史发言:
%s
场上存活玩家:%s
规则:
1. 绝对不能投票给自己(%d号);
2. 优先投票你怀疑的狼人;
3. 只回复1-12之间的数字,不要加任何文字、符号、括号;
4. 你的目标是投出狼人,不要投好人。
请请根据历史游戏信息和自己的判断,投票放逐你怀疑的狼人,只回复玩家编号(数字),不要多余内容。
""", p.getPlayerNo(), p.getRole(), getHistorySpeakText(roomId), alivePlayers.stream().map(item -> item.getPlayerNo().toString()).collect(Collectors.joining("、")), p.getPlayerNo());
}
String vote = aiPlayerService.aiAction(p, prompt).trim();
// 校验投票合法性:必须是数字、存活玩家、狼人不能投自己
if (vote.matches("\\d+") && aliveNos.contains(vote)) {
cnt.put(vote, cnt.getOrDefault(vote, 0) + 1);
}
}
}
// 取票数最多的目标(无有效投票则随机选一个非狼人存活玩家)
if (cnt.isEmpty()) {
List<String> targetNos = alivePlayers.stream()
.filter(p -> !p.getRole().equals(RoleEnum.WEREWOLF.name()))
.map(p -> p.getPlayerNo().toString())
.toList();
return targetNos.get(new Random().nextInt(targetNos.size()));
}
return cnt.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(null);
}
}
19.返回结果对象VO
package com.zhan.game.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GameStageAdvancePlayerVO {
private Integer playerNo;
private String role;
}
package com.zhan.game.vo;
import com.zhan.game.enums.GameResult;
import lombok.Data;
import java.util.List;
@Data
public class GameStageAdvanceVO {
private Long roomId;
private String stage;
private String stageMsg;
private GameResult gameResult;
private List<GameStageAdvancePlayerVO> votedDead;
private List<GameStageAdvancePlayerVO> deadLastNight;
}
20.工具类
package com.zhan.game.utils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class GameUtil {
public static String generateRoomName() {
// 获取当前系统时间
LocalDateTime now = LocalDateTime.now();
// 定义日期时间格式:yyyyMMddhhmmss
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
// 格式化时间并拼接前缀 "AI狼人杀房间"
return "AI狼人杀房间" + now.format(formatter);
}
}
21.系统日志(测试)
2026-02-12T18:03:18.366+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.game.service.impl.GameServiceImpl : 开始推进游戏阶段
2026-02-12T18:03:18.378+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.game.service.impl.GameServiceImpl : 房间37:开始执行完整夜晚流程
2026-02-12T18:03:18.392+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 11号玩家(守卫), 提示词:你是狼人杀守卫,根据历史游戏信息和自己的判断,今晚请选择一名玩家守护(不能连续两晚守同一个人)。
只回复数字,不要多余内容。
2026-02-12T18:03:19.769+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 11号玩家(守卫), 回复内容:7
2026-02-12T18:03:19.930+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 2号玩家(狼人), 提示词:【狼人晚上集体刀人】
你是2号玩家,身份是狼人,正在参与放逐投票。
历史发言:
暂无玩家发言
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
2026-02-12T18:03:20.696+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 2号玩家(狼人), 回复内容:3
2026-02-12T18:03:20.737+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 6号玩家(狼人), 提示词:【狼人晚上集体刀人】
你是6号玩家,身份是狼人,正在参与放逐投票。
历史发言:
暂无玩家发言
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
2026-02-12T18:03:21.952+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 6号玩家(狼人), 回复内容:2
2026-02-12T18:03:22.012+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 10号玩家(狼人), 提示词:【狼人晚上集体刀人】
你是10号玩家,身份是狼人,正在参与放逐投票。
历史发言:
暂无玩家发言
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
2026-02-12T18:03:22.888+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 10号玩家(狼人), 回复内容:7
2026-02-12T18:03:22.961+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 12号玩家(狼人), 提示词:【狼人晚上集体刀人】
你是12号玩家,身份是狼人,正在参与放逐投票。
历史发言:
暂无玩家发言
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
2026-02-12T18:03:23.617+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 12号玩家(狼人), 回复内容:7
2026-02-12T18:03:23.705+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 3号玩家(预言家), 提示词:你是预言家,晚上选择一名玩家查验身份。
规则:规则:只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
根据历史游戏信息和自己的判断,请直接回复目标玩家编号:
2026-02-12T18:03:24.478+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 3号玩家(预言家), 回复内容:2
2026-02-12T18:03:24.673+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 8号玩家(女巫), 提示词:你是8号女巫,当前解药可用、毒药已用。
今夜7号玩家被狼人刀杀,你是否使用解药拯救他?
注意:解药整局只能用1次,且不能和毒药同晚使用。
根据历史游戏信息和自己的判断,仅回复:是 或 否,不要多余内容。
2026-02-12T18:03:25.499+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 8号玩家(女巫), 回复内容:否
2026-02-12T18:03:25.603+08:00 INFO 24336 --- [http-nio-8080-exec-3] c.z.game.service.impl.GameServiceImpl : 房间37:夜间死亡玩家编号列表 -> []
2026-02-12T18:12:33.690+08:00 INFO 24336 --- [SpringApplicationShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
2026-02-12T18:12:34.486+08:00 INFO 24336 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown : Graceful shutdown complete
2026-02-12T18:12:34.495+08:00 INFO 24336 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2026-02-12T18:12:34.508+08:00 INFO 24336 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2026-02-12T18:12:40.710+08:00 INFO 26968 --- [main] com.zhan.game.GameApp : Starting GameApp using Java 17.0.17 with PID 26968 (D:\code99\werewolf-kill\target\classes started by 80008307 in D:\code99\werewolf-kill)
2026-02-12T18:12:40.714+08:00 INFO 26968 --- [main] com.zhan.game.GameApp : The following 1 profile is active: "dev"
2026-02-12T18:12:41.987+08:00 INFO 26968 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
2026-02-12T18:12:41.991+08:00 INFO 26968 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2026-02-12T18:12:42.023+08:00 INFO 26968 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 15 ms. Found 0 Redis repository interfaces.
2026-02-12T18:12:42.822+08:00 INFO 26968 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2026-02-12T18:12:42.836+08:00 INFO 26968 --- [main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2026-02-12T18:12:42.837+08:00 INFO 26968 --- [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.40]
2026-02-12T18:12:42.927+08:00 INFO 26968 --- [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2026-02-12T18:12:42.928+08:00 INFO 26968 --- [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2148 ms
2026-02-12T18:12:46.598+08:00 INFO 26968 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2026-02-12T18:12:46.615+08:00 INFO 26968 --- [main] com.zhan.game.GameApp : Started GameApp in 6.523 seconds (process running for 7.274)
2026-02-12T18:13:06.451+08:00 INFO 26968 --- [http-nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2026-02-12T18:13:06.451+08:00 INFO 26968 --- [http-nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2026-02-12T18:13:06.454+08:00 INFO 26968 --- [http-nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2026-02-12T18:13:06.540+08:00 INFO 26968 --- [http-nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-02-12T18:13:06.763+08:00 INFO 26968 --- [http-nio-8080-exec-1] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@7372be86
2026-02-12T18:13:06.765+08:00 INFO 26968 --- [http-nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-02-12T18:13:13.456+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.game.service.impl.GameServiceImpl : 开始推进游戏阶段
2026-02-12T18:13:13.462+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.game.service.impl.GameServiceImpl : 房间38:开始执行完整夜晚流程
2026-02-12T18:13:13.468+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 1号玩家(守卫), 提示词:你是狼人杀守卫,根据历史游戏信息和自己的判断,今晚请选择一名玩家守护(不能连续两晚守同一个人)。
只回复数字,不要多余内容。
2026-02-12T18:13:15.101+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 1号玩家(守卫), 回复内容:5
2026-02-12T18:13:15.206+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 5号玩家(狼人), 提示词:【狼人晚上集体刀人】
你是5号玩家,身份是狼人,正在参与放逐投票。
历史发言:
暂无玩家发言
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
2026-02-12T18:13:16.382+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 5号玩家(狼人), 回复内容:4
2026-02-12T18:13:16.433+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 7号玩家(狼人), 提示词:【狼人晚上集体刀人】
你是7号玩家,身份是狼人,正在参与放逐投票。
历史发言:
暂无玩家发言
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
2026-02-12T18:13:17.109+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 7号玩家(狼人), 回复内容:8
2026-02-12T18:13:17.149+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 10号玩家(狼人), 提示词:【狼人晚上集体刀人】
你是10号玩家,身份是狼人,正在参与放逐投票。
历史发言:
暂无玩家发言
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
2026-02-12T18:13:18.187+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 10号玩家(狼人), 回复内容:4
2026-02-12T18:13:18.256+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 12号玩家(狼人), 提示词:【狼人晚上集体刀人】
你是12号玩家,身份是狼人,正在参与放逐投票。
历史发言:
暂无玩家发言
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
2026-02-12T18:13:18.997+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 12号玩家(狼人), 回复内容:3
2026-02-12T18:13:19.037+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 6号玩家(预言家), 提示词:你是预言家,晚上选择一名玩家查验身份。
规则:规则:只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
根据历史游戏信息和自己的判断,请直接回复目标玩家编号:
2026-02-12T18:13:19.928+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 6号玩家(预言家), 回复内容:6
2026-02-12T18:13:20.098+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 9号玩家(女巫), 提示词:你是9号女巫,当前解药可用、毒药可用。
今夜4号玩家被狼人刀杀,你是否使用解药拯救他?
注意:解药整局只能用1次,且不能和毒药同晚使用。
根据历史游戏信息和自己的判断,仅回复:是 或 否,不要多余内容。
2026-02-12T18:13:20.753+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.g.service.impl.AiPlayerServiceImpl : 9号玩家(女巫), 回复内容:是
2026-02-12T18:13:20.794+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.game.service.impl.GameServiceImpl : 女巫9号使用解药拯救了4号玩家
2026-02-12T18:13:20.888+08:00 INFO 26968 --- [http-nio-8080-exec-3] c.z.game.service.impl.GameServiceImpl : 房间38:夜间死亡玩家编号列表 -> []
2026-02-12T18:18:26.556+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 开始推进游戏阶段
2026-02-12T18:18:26.639+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 昨夜平安夜
2026-02-12T18:18:26.826+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 1号玩家(守卫), 提示词:【狼人杀游戏信息】
你的身份:1号守卫
场上局势:
昨夜死亡玩家:
你昨夜守护的玩家:5
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
暂无玩家发言
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:18:30.438+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 1号玩家(守卫), 回复内容:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2026-02-12T18:18:30.518+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 1号玩家(守卫)发言:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2026-02-12T18:18:30.680+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 2号玩家(平民), 提示词:【狼人杀游戏信息】
你的身份:2号平民
场上局势:
昨夜死亡玩家:
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:18:32.979+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 2号玩家(平民), 回复内容:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
2026-02-12T18:18:33.031+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 2号玩家(平民)发言:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
2026-02-12T18:18:33.169+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 3号玩家(猎人), 提示词:【狼人杀游戏信息】
你的身份:3号猎人
场上局势:
昨夜死亡玩家:
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:18:37.159+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 3号玩家(猎人), 回复内容:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
2026-02-12T18:18:37.235+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 3号玩家(猎人)发言:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
2026-02-12T18:18:37.372+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 4号玩家(平民), 提示词:【狼人杀游戏信息】
你的身份:4号平民
场上局势:
昨夜死亡玩家:
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:18:39.411+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 4号玩家(平民), 回复内容:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
2026-02-12T18:18:39.454+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 4号玩家(平民)发言:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
2026-02-12T18:18:39.650+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 5号玩家(狼人), 提示词:【狼人杀游戏信息】
你的身份:5号狼人
场上局势:
昨夜死亡玩家:
你的狼人队友:5、7、10、12
昨夜狼人刀人目标:4
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:18:44.416+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 5号玩家(狼人), 回复内容:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
2026-02-12T18:18:44.493+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 5号玩家(狼人)发言:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
2026-02-12T18:18:44.672+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 6号玩家(预言家), 提示词:【狼人杀游戏信息】
你的身份:6号预言家
场上局势:
昨夜死亡玩家:
你昨夜查验6号玩家,身份是:好人
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:18:48.676+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 6号玩家(预言家), 回复内容:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
2026-02-12T18:18:48.755+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 6号玩家(预言家)发言:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
2026-02-12T18:18:48.961+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 7号玩家(狼人), 提示词:【狼人杀游戏信息】
你的身份:7号狼人
场上局势:
昨夜死亡玩家:
你的狼人队友:5、7、10、12
昨夜狼人刀人目标:4
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:18:53.533+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 7号玩家(狼人), 回复内容:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
2026-02-12T18:18:53.634+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 7号玩家(狼人)发言:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
2026-02-12T18:18:53.812+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 8号玩家(平民), 提示词:【狼人杀游戏信息】
你的身份:8号平民
场上局势:
昨夜死亡玩家:
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:18:55.803+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 8号玩家(平民), 回复内容:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
2026-02-12T18:18:55.847+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 8号玩家(平民)发言:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
2026-02-12T18:18:56.060+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 9号玩家(女巫), 提示词:【狼人杀游戏信息】
你的身份:9号女巫
场上局势:
昨夜死亡玩家:
你的解药状态:已用
你的毒药状态:可用
昨夜被刀玩家:4
你昨夜用药情况:使用解药拯救4号玩家
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:18:59.825+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 9号玩家(女巫), 回复内容:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
2026-02-12T18:18:59.910+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 9号玩家(女巫)发言:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
2026-02-12T18:19:00.094+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 10号玩家(狼人), 提示词:【狼人杀游戏信息】
你的身份:10号狼人
场上局势:
昨夜死亡玩家:
你的狼人队友:5、7、10、12
昨夜狼人刀人目标:4
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:19:04.194+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 10号玩家(狼人), 回复内容:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
2026-02-12T18:19:04.276+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 10号玩家(狼人)发言:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
2026-02-12T18:19:04.412+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 11号玩家(平民), 提示词:【狼人杀游戏信息】
你的身份:11号平民
场上局势:
昨夜死亡玩家:
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:19:06.409+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 11号玩家(平民), 回复内容:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
2026-02-12T18:19:06.447+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 11号玩家(平民)发言:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
2026-02-12T18:19:06.638+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 12号玩家(狼人), 提示词:【狼人杀游戏信息】
你的身份:12号狼人
场上局势:
昨夜死亡玩家:
你的狼人队友:5、7、10、12
昨夜狼人刀人目标:4
请根据场上局势、其他人发言和你的身份进行逻辑发言。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
11号:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
发言要求:
1. 请根据场上局势、其他人发言和你的身份进行逻辑发言
2. 狼人要隐藏身份,好人要找出狼人
3. 发言字数50-120字,只回复发言内容,不要多余文字
4. 不要说自己是AI,不要解释规则
5. 不要加任何前缀,比如“以XX视角”,直接开始说内容
6. 语言像真人玩家一样自然、简短、有逻辑
7. 可以分析、站队、怀疑、保人、点狼坑
2026-02-12T18:19:09.841+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 12号玩家(狼人), 回复内容:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
2026-02-12T18:19:09.923+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.game.service.impl.GameServiceImpl : 12号玩家(狼人)发言:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
2026-02-12T18:19:10.044+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 1号玩家(守卫), 提示词:【狼人杀公投】
你是1号玩家,身份是守卫(好人),正在参与放逐投票。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
11号:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
12号:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1. 绝对不能投票给自己(1号);
2. 优先投票你怀疑的狼人;
3. 只回复1-12之间的数字,不要加任何文字、符号、括号;
4. 你的目标是投出狼人,不要投好人。
请请根据历史游戏信息和自己的判断,投票放逐你怀疑的狼人,只回复玩家编号(数字),不要多余内容。
2026-02-12T18:19:10.728+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 1号玩家(守卫), 回复内容:6
2026-02-12T18:19:10.805+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 2号玩家(平民), 提示词:【狼人杀公投】
你是2号玩家,身份是平民(好人),正在参与放逐投票。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
11号:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
12号:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1. 绝对不能投票给自己(2号);
2. 优先投票你怀疑的狼人;
3. 只回复1-12之间的数字,不要加任何文字、符号、括号;
4. 你的目标是投出狼人,不要投好人。
请请根据历史游戏信息和自己的判断,投票放逐你怀疑的狼人,只回复玩家编号(数字),不要多余内容。
2026-02-12T18:19:11.377+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 2号玩家(平民), 回复内容:6
2026-02-12T18:19:11.443+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 3号玩家(猎人), 提示词:【狼人杀公投】
你是3号玩家,身份是猎人(好人),正在参与放逐投票。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
11号:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
12号:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1. 绝对不能投票给自己(3号);
2. 优先投票你怀疑的狼人;
3. 只回复1-12之间的数字,不要加任何文字、符号、括号;
4. 你的目标是投出狼人,不要投好人。
请请根据历史游戏信息和自己的判断,投票放逐你怀疑的狼人,只回复玩家编号(数字),不要多余内容。
2026-02-12T18:19:12.570+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 3号玩家(猎人), 回复内容:6
2026-02-12T18:19:12.638+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 4号玩家(平民), 提示词:【狼人杀公投】
你是4号玩家,身份是平民(好人),正在参与放逐投票。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
11号:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
12号:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1. 绝对不能投票给自己(4号);
2. 优先投票你怀疑的狼人;
3. 只回复1-12之间的数字,不要加任何文字、符号、括号;
4. 你的目标是投出狼人,不要投好人。
请请根据历史游戏信息和自己的判断,投票放逐你怀疑的狼人,只回复玩家编号(数字),不要多余内容。
2026-02-12T18:19:13.190+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 4号玩家(平民), 回复内容:6
2026-02-12T18:19:13.294+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 5号玩家(狼人), 提示词:【狼人晚上集体刀人】
你是5号玩家,身份是狼人,正在参与放逐投票。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
11号:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
12号:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
2026-02-12T18:19:13.946+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 5号玩家(狼人), 回复内容:6
2026-02-12T18:19:14.017+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 6号玩家(预言家), 提示词:【狼人杀公投】
你是6号玩家,身份是预言家(好人),正在参与放逐投票。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
11号:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
12号:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1. 绝对不能投票给自己(6号);
2. 优先投票你怀疑的狼人;
3. 只回复1-12之间的数字,不要加任何文字、符号、括号;
4. 你的目标是投出狼人,不要投好人。
请请根据历史游戏信息和自己的判断,投票放逐你怀疑的狼人,只回复玩家编号(数字),不要多余内容。
2026-02-12T18:19:14.728+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 6号玩家(预言家), 回复内容:1
2026-02-12T18:19:14.797+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 7号玩家(狼人), 提示词:【狼人晚上集体刀人】
你是7号玩家,身份是狼人,正在参与放逐投票。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
11号:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
12号:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
2026-02-12T18:19:16.043+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 7号玩家(狼人), 回复内容:6
2026-02-12T18:19:16.104+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 8号玩家(平民), 提示词:【狼人杀公投】
你是8号玩家,身份是平民(好人),正在参与放逐投票。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
11号:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
12号:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1. 绝对不能投票给自己(8号);
2. 优先投票你怀疑的狼人;
3. 只回复1-12之间的数字,不要加任何文字、符号、括号;
4. 你的目标是投出狼人,不要投好人。
请请根据历史游戏信息和自己的判断,投票放逐你怀疑的狼人,只回复玩家编号(数字),不要多余内容。
2026-02-12T18:19:16.664+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 8号玩家(平民), 回复内容:6
2026-02-12T18:19:16.737+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 9号玩家(女巫), 提示词:【狼人杀公投】
你是9号玩家,身份是女巫(好人),正在参与放逐投票。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
11号:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
12号:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1. 绝对不能投票给自己(9号);
2. 优先投票你怀疑的狼人;
3. 只回复1-12之间的数字,不要加任何文字、符号、括号;
4. 你的目标是投出狼人,不要投好人。
请请根据历史游戏信息和自己的判断,投票放逐你怀疑的狼人,只回复玩家编号(数字),不要多余内容。
2026-02-12T18:19:17.627+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 9号玩家(女巫), 回复内容:6
2026-02-12T18:19:17.721+08:00 INFO 26968 --- [http-nio-8080-exec-6] c.z.g.service.impl.AiPlayerServiceImpl : 10号玩家(狼人), 提示词:【狼人晚上集体刀人】
你是10号玩家,身份是狼人,正在参与放逐投票。
历史发言:
1号:昨夜我守了5号,看来狼人刀了别人。5号现在应该是好人身份。昨晚空刀可能性不大,可能刀到了女巫或者猎人身上。现在需要重点听前置位的发言,看看谁在划水或者刻意带节奏。目前信息不多,我会重点注意对5号态度暧昧的玩家,狼人可能想脏5号身份。
2号:1号守5号?听着像真预言家报查验,但得防狼悍跳。5号如果真是金水先保着,但别急着站死队。现在狼可能在后置位憋刀,尤其要盯紧对5号阴阳怪气的。我暂时跟1号盘逻辑,看他后面怎么带节奏。
3号:1号跳守卫说守5号,那5号暂时当好人看。狼昨晚空刀可能不大,大概率刀在神身上,女巫没救人?今天出人可以保守点,先听后面发言。2号对1号态度有点暧昧,像在跟风但没完全认守卫,需要再听。我是强神,不急着跳,但狼如果抿身份可能会针对我。建议好人别乱炸身份,看看谁对5号有敌意。
4号:1号守5号听着合理,但得防狼悍跳。2、3号对1号态度都偏谨慎,尤其3号说“强神不跳”,有点刻意强调身份嫌疑。我暂跟1号盘逻辑,重点看谁急着带节奏出5号,狼大概率在后置位憋刀,今天先听4、5、6怎么聊。
5号:1号守我信息有点突然,暂时信他是真守卫。昨晚倒牌没出平安夜,女巫没救可能毒了人,或者狼刀在了神身上。2号、3号都在保1号但态度微妙,3号跳强神太急了,像在躲推。4号提到狼可能后置位憋刀,这点我认同,建议重点听5、6、7的发言。目前局势不明,好人别急着站队,尤其警惕对1号无条件跟风的。
6号:我才是真预言家,昨晚验了6号是好人。1号跳守卫但守5号逻辑矛盾,如果他是真守卫,狼人空刀概率极小。而且我作为6号预言家,验自己虽无收益,却印证1号信息有假。前置位2、3、4都在跟风1号,但3号强神跳得突兀,可能想躲推。建议好人回头,今天先出1号,我今晚会验5号身份。狼队可能在1、3、4里。
7号:6号说验自己就很离谱,预言家怎么可能浪费第一验查自己?明显悍跳。而且1号守卫逻辑清晰,守5号信息连贯,狼人空刀可能性很低。前置位都在盘正逻辑,6号却要出1号,强打抗推位,狼面很大。我建议今天先出6号,他如果是狼,狼队就是6号带的头,后面可能还有冲锋狼。女巫和猎人可以藏好,守卫继续守好关键位置。
8号:6号验自己太假,预言家第一晚不可能这么浪费验人。1号守5号逻辑通顺,狼大概率没空刀。7号分析到位,6号急着出1号像冲锋狼。我跟7号票6号,今天先出他,看今晚倒牌情况再盘。
9号:6号悍跳无疑,验自己完全不符合预言家行为。1号守卫信息与我掌握的昨夜刀口4号一致,证明1号是真守卫,4号是我用解药救的。狼队昨晚刀4没刀死,今天想推1号悍跳守卫,6号明显是冲锋狼。3号强神跳得有点早,但暂不定义。今天必须出6号,女巫毒药在手,狼队别想躲刀。
10号:6号悍跳预言家太明显了,验自己完全不符合预言家收益。9号跳女巫报刀口救了4号,和1号守卫信息能对应上,说明1、9两张神牌基本坐实。今天肯定要出6号,他的狼同伴可能在带节奏保他的人里。前置位3号强神跳得有点突兀,但暂时先不点。好人别分票,集中出6。
11号:9号女巫跳得及时,和1号守卫信息对上了,基本坐实神牌。6号验自己太假,铁狼冲锋。今天必须出6,他的狼队友可能在3、4里带节奏保他。好人别分票,先清明面狼,晚上再盘身份。
12号:6号验自己确实不像真预言家,而且9号女巫跳出来救了4号,和1号守卫的逻辑能对上,神牌面很大。今天出6没问题,但7号之前也踩6踩得很凶,不排除是狼踩狼做身份。好人别分票,但明天要仔细盘7号和8号的发言,可能有倒钩狼在里面。
场上存活玩家:1、2、3、4、5、6、7、8、9、10、11、12
规则:
1.优先投票你识别的好人/神牌
2.只能回复1-12之间的数字,不要任何多余内容(如文字、符号、空格)。
3.优先投票你识别的好人/神牌。
4.你的目标是投出好人,保护狼人队友。
请根据历史游戏信息和自己的判断,直接回复你要投票的玩家编号:
2026-02-12T18:19:17.910+08:00 INFO 26968 --- [SpringApplicationShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete